From 7239eff4b4326514bea4b4270bc4fd2e41ffe81f Mon Sep 17 00:00:00 2001 From: Louis Ouellet Date: Fri, 9 Jan 2026 14:37:04 -0500 Subject: [PATCH 01/51] General: Initial Commit --- .editorconfig | 22 + .github/FUNDING.yml | 3 + .github/ISSUE_TEMPLATE/bug_report.md | 31 ++ .github/ISSUE_TEMPLATE/feature_request.md | 20 + .github/no-response.yml | 13 + .github/workflows/release.yml | 60 +++ .gitignore | 157 ++----- .gitmodules | 4 + README.md | 43 +- SECURITY.md | 10 + VERSION | 1 + build.sh | 397 +++++++++++++++++ src/core | 1 + src/icons/icon.icns | Bin 0 -> 205240 bytes src/icons/icon.png | Bin 0 -> 6287 bytes src/icons/icon.svg | 46 ++ src/main.py | 51 +++ src/replicator/replicator.py | 507 ++++++++++++++++++++++ update.sh | 7 + 19 files changed, 1258 insertions(+), 115 deletions(-) create mode 100644 .editorconfig create mode 100644 .github/FUNDING.yml create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md create mode 100644 .github/no-response.yml create mode 100644 .github/workflows/release.yml create mode 100644 .gitmodules create mode 100644 SECURITY.md create mode 100644 VERSION create mode 100755 build.sh create mode 160000 src/core create mode 100644 src/icons/icon.icns create mode 100644 src/icons/icon.png create mode 100644 src/icons/icon.svg create mode 100755 src/main.py create mode 100644 src/replicator/replicator.py create mode 100755 update.sh diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..bdaa458 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,22 @@ +; http://editorconfig.org/ + +root = true + +[*] +indent_style = space +indent_size = 4 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.{yml,yaml}] +indent_size = 2 + +[{vendor,inc/phpseclib}/**] +; Use editor default (possible autodetection). +indent_style = +indent_size = +end_of_line = +trim_trailing_whitespace = +insert_final_newline = \ No newline at end of file diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..b702f28 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,3 @@ +# These are supported funding model platforms + +github: [LaswitchTech] diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..3b6bc43 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,31 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: bug +assignees: '' + +--- + +### Description + +[Description of the bug or feature] + +### Steps to reproduce + +1. [First Step] +2. [Second Step] +3. [and so on...] + +**Expected behavior:** [What you expected to happen] + +**Actual behavior:** [What actually happened] + +### Versions + +* [PHP] +* [Browser] + +### Screenshots or Logs + +[Paste your logs or attach the screenshot] diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..191ad50 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: feature request +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. \ No newline at end of file diff --git a/.github/no-response.yml b/.github/no-response.yml new file mode 100644 index 0000000..c7c7e69 --- /dev/null +++ b/.github/no-response.yml @@ -0,0 +1,13 @@ +# Configuration for probot-no-response - https://github.com/probot/no-response + +# Number of days of inactivity before an Issue is closed for lack of response +daysUntilClose: 14 +# Label requiring a response +responseRequiredLabel: need more info +# Comment to post when closing an Issue for lack of response. Set to `false` to disable +closeComment: > + This issue has been automatically closed because there has been no response + to our request for more information from the original author. With only the + information that is currently in the issue, we don't have enough information + to take action. Please reach out if you have or find the answers we need so + that we can investigate further. \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..4cde3ed --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,60 @@ +name: Release + +on: + push: + tags: + - 'v*' + +jobs: + release: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v2 + with: + fetch-depth: 0 + + - name: Set Tag as Filename + id: tag_name + run: echo "TAG_NAME=${GITHUB_REF##*/}" >> $GITHUB_ENV + + - name: Create ZIP file + run: zip -r "${{ env.TAG_NAME }}.zip" . + + - name: Generate Changelog + id: generate_changelog + run: | + # Find the most recent tag before the current one + PREV_TAG=$(git describe --tags --abbrev=0 HEAD^) + + # Create a new CHANGELOG.md file with headers + echo -e "# Changelog\n" > CHANGELOG.md + + # List commit messages between the previous tag and current HEAD + git log ${PREV_TAG}..HEAD --pretty=format:"* %s" >> CHANGELOG.md + + # List unique contributors for these commits + echo -e "\n\n# Contributors\n" >> CHANGELOG.md + git log ${PREV_TAG}..HEAD --format='%aN' | sort -u | awk '{print "* " $0}' >> CHANGELOG.md + + - name: Create Release + id: create_release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GH_PAT }} + with: + tag_name: ${{ github.ref }} + release_name: Release ${{ github.ref }} + draft: false + prerelease: false + body_path: ./CHANGELOG.md + + - name: Upload Asset + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GH_PAT }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: ./${{ env.TAG_NAME }}.zip + asset_name: source.zip + asset_content_type: application/zip diff --git a/.gitignore b/.gitignore index b7faf40..54779cc 100644 --- a/.gitignore +++ b/.gitignore @@ -1,16 +1,42 @@ +# Python +/build/ + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ +TOKEN +runtime + +# Mac OS X +.DS_Store +*.DS_Store + +# Git +.git + +# Filetypes +*.cfg +*.log + +# Unique Directories +/tmp/ + # Byte-compiled / optimized / DLL files __pycache__/ -*.py[codz] +*.py[cod] *$py.class -# C extensions -*.so - # Distribution / packaging .Python build/ develop-eggs/ -dist/ +config/ +logs/ downloads/ eggs/ .eggs/ @@ -46,7 +72,7 @@ htmlcov/ nosetests.xml coverage.xml *.cover -*.py.cover +*.py,cover .hypothesis/ .pytest_cache/ cover/ @@ -82,47 +108,13 @@ target/ profile_default/ ipython_config.py -# pyenv -# For a library or package, you might want to ignore these files since the code is -# intended to run in multiple environments; otherwise, check them in: -# .python-version - -# pipenv -# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. -# However, in case of collaboration, if having platform-specific dependencies or dependencies -# having no cross-platform support, pipenv may install dependencies that don't work, or not -# install all needed dependencies. -#Pipfile.lock - -# UV -# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control. -# This is especially recommended for binary packages to ensure reproducibility, and is more -# commonly ignored for libraries. -#uv.lock - -# poetry -# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. -# This is especially recommended for binary packages to ensure reproducibility, and is more -# commonly ignored for libraries. -# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control -#poetry.lock -#poetry.toml - # pdm # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. -# pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python. -# https://pdm-project.org/en/latest/usage/project/#working-with-version-control #pdm.lock -#pdm.toml -.pdm-python -.pdm-build/ - -# pixi -# Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control. -#pixi.lock -# Pixi creates a virtual environment in the .pixi directory, just like venv module creates one -# in the .venv directory. It is recommended not to include this directory in version control. -.pixi +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm __pypackages__/ @@ -134,74 +126,11 @@ celerybeat.pid # SageMath parsed files *.sage.py -# Environments -.env -.envrc -.venv -env/ -venv/ -ENV/ -env.bak/ -venv.bak/ +# building source files +src/bin/freerdp/source +src/bin/freerdp/install +src/bin/freerdp/build -# Spyder project settings -.spyderproject -.spyproject - -# Rope project settings -.ropeproject - -# mkdocs documentation -/site - -# mypy -.mypy_cache/ -.dmypy.json -dmypy.json - -# Pyre type checker -.pyre/ - -# pytype static type analyzer -.pytype/ - -# Cython debug symbols -cython_debug/ - -# PyCharm -# JetBrains specific template is maintained in a separate JetBrains.gitignore that can -# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore -# and can be added to the global gitignore or merged into this file. For a more nuclear -# option (not recommended) you can uncomment the following to ignore the entire idea folder. -#.idea/ - -# Abstra -# Abstra is an AI-powered process automation framework. -# Ignore directories containing user credentials, local state, and settings. -# Learn more at https://abstra.io/docs -.abstra/ - -# Visual Studio Code -# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore -# that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore -# and can be added to the global gitignore or merged into this file. However, if you prefer, -# you could uncomment the following to ignore the entire vscode folder -# .vscode/ - -# Ruff stuff: -.ruff_cache/ - -# PyPI configuration file -.pypirc - -# Cursor -# Cursor is an AI-powered code editor. `.cursorignore` specifies files/directories to -# exclude from AI features like autocomplete and code analysis. Recommended for sensitive data -# refer to https://docs.cursor.com/context/ignore-files -.cursorignore -.cursorindexingignore - -# Marimo -marimo/_static/ -marimo/_lsp/ -__marimo__/ +# Exclusions +!src/bin/freerdp/*/*/lib/ +!src/bin/freerdp/*/*/xfreerdp diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..137e0dc --- /dev/null +++ b/.gitmodules @@ -0,0 +1,4 @@ +[submodule "src/core"] + path = src/core + url = https://github.com/LaswitchTech/corePY.git + branch = dev diff --git a/README.md b/README.md index 714570a..2589e5c 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,43 @@ +

+ # Replicator -Data Replication Software, Replacement for Windows DFSR with support for Local, SMB and FTP filesystems. +![License](https://img.shields.io/github/license/LaswitchTech/Replicator?style=for-the-badge) +![GitHub repo size](https://img.shields.io/github/repo-size/LaswitchTech/Replicator?style=for-the-badge&logo=github) +![GitHub top language](https://img.shields.io/github/languages/top/LaswitchTech/Replicator?style=for-the-badge) +![GitHub Downloads](https://img.shields.io/github/downloads/LaswitchTech/Replicator/total?style=for-the-badge) +![Version](https://img.shields.io/github/v/release/LaswitchTech/Replicator?label=Version&style=for-the-badge) + +## Description +Replicator is a cross-platform Python application designed to provide replication services similar to Windows DFSR. It supports local filesystems, SMB, and FTP, making it a versatile solution for data replication needs across different operating systems. + +## Features + - **Cross-Platform Compatibility**: Replicator is compatible with Windows, macOS and Linux, with specific adjustments made to ensure seamless operation on both operating systems. + - **Customizable Interface**: The application uses a customizable UI that allows users to define their preferred settings. + - **Logging and Debugging**: The application includes logging features for easier debugging and tracking of issues during the replication process. + +## License +This software is distributed under the [GPLv3](LICENSE) license. + +## Security +Please disclose any vulnerabilities found responsibly – report security issues to the maintainers privately. See [SECURITY.md](SECURITY.md) for more information. + +## Contributing +Contributions to Replicator are welcome! If you have ideas for new features or have found bugs, please open an issue or submit a pull request. + +### How to Contribute + - **Fork the Repository**: Create a fork of the repository on GitHub. + - **Create a New Branch**: For new features or bug fixes, create a new branch in your fork. + - **Submit a Pull Request**: Once your changes are ready, submit a pull request to the main repository. + +## To Do + - **Support for Local**: Add support for Local filesystems. + - **Support for SMB**: Add support for SMB Shares. + - **Support for FTP**: Add support for FTP Shares. + - **Mode Mirror**: Add replication mode Mirror. + - **Mode Incremental**: Add replication mode Incremental. + - **Direction**: Add replication direction (One way, Two way). + - **Scheduling**: Add scheduling capabilities for replication tasks. + - **Conflict Resolution**: Implement conflict resolution strategies for file changes. + +## Wait, where is the documentation? +Review the [Documentation](https://laswitchtech.com/en/blog/projects/pyrdpconnect/index). diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..86201e0 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,10 @@ +# Security Policy + +Security vulnerabilities can be reported for the current stable release and the `stable` branch. + +## Reporting a Vulnerability + +You have multiple options on reporting vulnerabilities + +* Send an e-mail to [Support Team](mailto:support@laswitchtech.com) +* Open a [Github Issue](https://github.com/LaswitchTech/Replicator/issues) diff --git a/VERSION b/VERSION new file mode 100644 index 0000000..45c7a58 --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +v0.0.1 diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..87b99ac --- /dev/null +++ b/build.sh @@ -0,0 +1,397 @@ +#!/bin/bash +set -euo pipefail + +# Function to print messages with a timestamp +log() { + echo "$(date +'%Y-%m-%d %H:%M:%S') - $1" +} + +# Function to detect the operating system +detect_os() { + case "$(uname -s)" in + Darwin) + echo "macos" + ;; + Linux) + echo "linux" + ;; + *) + echo "unsupported" + ;; + esac +} + +# Set the name of the application +NAME="PyRDPConnect" + +# Determine the operating system +OS=$(detect_os) + +if [ "$OS" == "unsupported" ]; then + log "Unsupported operating system. Exiting." + exit 1 +fi + +# Determine arch and whether we should use system PyQt5 (APT) on Linux ARM +ARCH="$(uname -m)" +USE_SYSTEM_PYQT=0 +if [ "$OS" = "linux" ] && { [ "$ARCH" = "aarch64" ] || [ "$ARCH" = "armv7l" ] || [ "$ARCH" = "armhf" ]; }; then + USE_SYSTEM_PYQT=1 +fi + +# Path to a vendored FreeRDP binary (set on macOS; Linux uses --add-data and resolves at runtime) +FREERDP_BIN="" + +# Create a directory to store the final output based on the OS +FINAL_DIR="dist/$OS" +if [ -d "$FINAL_DIR" ]; then + rm -rf "$FINAL_DIR" +fi +mkdir -p "$FINAL_DIR" + +# Require python3.11 on PATH +PYTHON_BIN="$(command -v python3.11 || true)" +if [ -z "$PYTHON_BIN" ]; then + log "python3.11 not found. On macOS, run: brew install python@3.11" + exit 1 +fi + +# (Re)create venv if missing or wrong version +NEED_RECREATE=0 +if [ ! -x "env/bin/python" ]; then + NEED_RECREATE=1 +else + VENV_VER="$(env/bin/python -c 'import sys; print(f"{sys.version_info.major}.{sys.version_info.minor}")' || echo unknown)" + if [ "$VENV_VER" != "3.11" ]; then + NEED_RECREATE=1 + fi +fi + +if [ "$NEED_RECREATE" -eq 1 ]; then + log "Creating fresh Python 3.11 virtual environment..." + rm -rf env + if [ "$USE_SYSTEM_PYQT" -eq 1 ]; then + # allow APT-installed PyQt5 to be visible in the venv + "$PYTHON_BIN" -m venv --system-site-packages env + else + "$PYTHON_BIN" -m venv env + fi +fi + +# Activate venv +# shellcheck disable=SC1091 +source env/bin/activate + +# Double-check version (hard fail if not 3.11) +ACTIVE_VER="$(python -c 'import sys; print(f"{sys.version_info.major}.{sys.version_info.minor}")')" +if [ "$ACTIVE_VER" != "3.11" ]; then + log "Active Python is $ACTIVE_VER, expected 3.11. Aborting." + exit 1 +fi +log "Using Python $(python -V)" + +# Ensure that pip is updated +log "Updating pip..." +python -m pip install --upgrade pip wheel + +log "Installing build dependencies..." +# PyInstaller 6.9+ supports 3.11 well; lock to <7 to avoid future surprizes. +# PyQt5 5.15.x is stable for Qt5 on macOS/Linux; lock <6. +python -m pip install "pyinstaller>=6.9,<7" "sip>=6.9,<7" + +if [ "$USE_SYSTEM_PYQT" -eq 1 ]; then + # Ensure system PyQt5 (and QtSvg) are present + if command -v apt-get >/dev/null 2>&1; then + log "Installing system PyQt5 via APT (requires sudo)..." + sudo apt-get update + sudo apt-get install -y python3-pyqt5 python3-pyqt5.qtsvg + else + log "ERROR: APT not found; cannot install system PyQt5. Install PyQt5 manually or switch to a distro with APT." + exit 1 + fi + + # ensure system dist-packages are visible inside the venv (Debian/RPi) + SYS_PYTHON="$(command -v python3)" + + # Collect all system "dist-packages" dirs that might contain APT's PyQt5 + SYS_DIST_DIRS="$("$SYS_PYTHON" - <<'PY' +import site, sys +paths=set() + +# Prefer entries that actually end with dist-packages +for p in getattr(site, 'getsitepackages', lambda: [])() or []: + if p.endswith('dist-packages'): + paths.add(p) + +up = getattr(site, 'getusersitepackages', lambda: None)() +if up and str(up).endswith('dist-packages'): + paths.add(up) + +# Common Debian/RPi locations +for c in ('/usr/lib/python3/dist-packages', '/usr/local/lib/python3/dist-packages'): + paths.add(c) + +print('\\n'.join(sorted(paths))) +PY + )" + + # Path to *this venv's* site-packages + VENV_SITE_PKGS="$(python - <<'PY' +import sysconfig +print(sysconfig.get_paths()["purelib"]) +PY + )" + + # Write a .pth pointing to each system dist-packages dir + : > "$VENV_SITE_PKGS/_system_dist_packages.pth" + while IFS= read -r d; do + [ -n "$d" ] && echo "$d" >> "$VENV_SITE_PKGS/_system_dist_packages.pth" + done <<< "$SYS_DIST_DIRS" + + # Hard guarantee via sitecustomize.py + cat > "$VENV_SITE_PKGS/sitecustomize.py" <<'PY' +import sys +NEED = [ + '/usr/lib/python3/dist-packages', + '/usr/local/lib/python3/dist-packages', +] +for p in NEED: + if p not in sys.path: + sys.path.append(p) +PY + + # Sanity check (now should succeed) + python - <<'PY' +import sys +try: + import PyQt5, PyQt5.QtCore, PyQt5.QtWidgets, PyQt5.QtSvg + print("OK: System PyQt5 detected at:", PyQt5.__file__) +except Exception as e: + print("sys.path =", sys.path) + raise SystemExit(f"PyQt5 missing after setup: {e}") +PY +else + # Non-ARM / macOS etc: keep using PyPI wheels + python -m pip install "PyQt5>=5.15,<6" +fi + +# Optional tools you had; keeping them only if you need them: +python -m pip install PySide6-Addons +# python -m pip install importlib PySide6-Addons + +# Check if the .spec file exists +SPEC_FILE="$NAME.spec" +ICON_FILE="src/icons/icon.icns" + +# Cleanup: Remove the leftover dist/$NAME directory on macOS +log "Cleaning up..." +if [ -d "dist/$NAME" ]; then + rm -rf "dist/$NAME" +fi +if [ -f "$SPEC_FILE" ]; then + rm -f "$SPEC_FILE" +fi + +log "Verifying required PyQt5 modules are present..." +python - <<'PY' +from importlib.util import find_spec +missing = [m for m in ("PyQt5", "PyQt5.QtSvg") if find_spec(m) is None] +if missing: + raise SystemExit(f"Missing modules before build: {missing}") +print("Qt check passed.") +PY + +log ".spec file not found. Generating a new one with PyInstaller..." +if [ "$OS" == "macos" ]; then + pyinstaller --windowed --name "$NAME" src/PyRDPConnect.py +elif [ "$OS" == "linux" ]; then + # Linux build: bundle data and hidden import directly + pyinstaller \ + --onefile \ + --name "$NAME" \ + --hidden-import PyQt5.QtSvg \ + --add-data "src/app:app" \ + --add-data "src/styles:styles" \ + --add-data "src/icons:icons" \ + --add-data "src/img:img" \ + --add-data "src/freerdp/linux:freerdp/linux" \ + src/PyRDPConnect.py +fi + +# Ensure the spec file now exists +if [ ! -f "$SPEC_FILE" ]; then + log "Failed to create .spec file. Exiting." + exit 1 +fi + +log "Generated .spec file: $SPEC_FILE" + +# Update the .spec file to include the custom icon, data files, and hidden imports +log "Updating the .spec file to include the custom icon, data files, and hidden imports..." +if [ "$OS" == "macos" ]; then + sed -i '' "s|icon=None|icon='$ICON_FILE'|g" $SPEC_FILE + sed -i '' "/Analysis/s/(.*)/\0, hiddenimports=['PyQt5.QtSvg']/" $SPEC_FILE + sed -i '' "/a.datas +=/a \\ + datas=[('src/styles', 'styles'), ('src/icons', 'icons'), ('src/app', 'app'), ('src/img', 'img')], + " $SPEC_FILE +elif [ "$OS" == "linux" ]; then + sed -i "s|icon=None|icon='$ICON_FILE'|g" $SPEC_FILE + sed -i "/Analysis/s/(.*)/\0, hiddenimports=['PyQt5.QtSvg']/" $SPEC_FILE + sed -i "/a.datas +=/a \\ + datas=[('src/styles', 'styles'), ('src/icons', 'icons'), ('src/app', 'app'), ('src/img', 'img')], + " $SPEC_FILE +fi + +# Build the project with PyInstaller using the updated .spec file +log "Building the project with PyInstaller..." +pyinstaller --noconfirm $SPEC_FILE + +# Copy resources into the appropriate location +if [ "$OS" == "macos" ]; then + APP_ROOT="dist/$NAME.app/Contents" + APP_MACOS="$APP_ROOT/MacOS" + APP_RES="$APP_ROOT/Resources" + + log "Creating app resource directories..." + mkdir -p "$APP_RES/styles" "$APP_RES/img" "$APP_RES/icons" + cp -R src/styles/* "$APP_RES/styles/" + cp -R src/img/* "$APP_RES/img/" + cp -R src/icons/* "$APP_RES/icons/" + + # ---- Bundle FreeRDP as PyRDPConnect expects (Resources/freerdp/macos/...) ---- + VENDOR_DIR_SRC="src/freerdp/macos" + VENDOR_DIR_DST="$APP_RES/freerdp/macos" + mkdir -p "$VENDOR_DIR_DST" + log "Copying FreeRDP tree to Resources..." + rsync -a "$VENDOR_DIR_SRC/" "$VENDOR_DIR_DST/" + + FREERDP_BIN="$VENDOR_DIR_DST/xfreerdp" + LIB_DIR="$VENDOR_DIR_DST/lib" + X11_DIR="$VENDOR_DIR_DST/x11" # optional (if you vendored X11 libs) + PLUGINS_DIR="$VENDOR_DIR_DST/plugins" # optional + + chmod +x "$FREERDP_BIN" + + # ---- Patch rpaths + rewrite absolute Homebrew refs -> @rpath/ ---- + BREW_PREFIX="$(brew --prefix 2>/dev/null || echo /opt/homebrew)" + + add_rpath_if_missing() { + local bin="$1" r="$2" + if ! otool -l "$bin" | awk '/LC_RPATH/{getline; print $2}' | grep -qx "$r"; then + install_name_tool -add_rpath "$r" "$bin" 2>/dev/null || true + fi + } + + # We want dyld to look inside ../lib and ../x11 relative to xfreerdp + add_rpath_if_missing "$FREERDP_BIN" "@loader_path/../lib" + if [ -d "$X11_DIR" ]; then + add_rpath_if_missing "$FREERDP_BIN" "@loader_path/../x11" + fi + + # For each lib we ship, set id to @rpath/ and rewrite any Homebrew absolute deps to @rpath/ + patch_one_file() { + local file="$1" + # set its own id (for dylibs) + if [[ "$file" == *.dylib ]]; then + install_name_tool -id "@rpath/$(basename "$file")" "$file" 2>/dev/null || true + fi + # rewrite deps + otool -L "$file" | awk 'NR>1{print $1}' | while read -r dep; do + [ -z "$dep" ] && continue + case "$dep" in + /System/*|/usr/lib/*) continue ;; # keep system libs + esac + if echo "$dep" | grep -Eq "^$BREW_PREFIX/(opt|Cellar)/"; then + base="$(basename "$dep")" + install_name_tool -change "$dep" "@rpath/$base" "$file" 2>/dev/null || true + fi + done + } + + log "Patching bundled dylibs..." + if [ -d "$LIB_DIR" ]; then + find "$LIB_DIR" -type f -name "*.dylib" -print0 | while IFS= read -r -d '' f; do + chmod u+w "$f" + patch_one_file "$f" + done + fi + if [ -d "$X11_DIR" ]; then + find "$X11_DIR" -type f -name "*.dylib" -print0 | while IFS= read -r -d '' f; do + chmod u+w "$f" + patch_one_file "$f" + done + fi + + log "Patching xfreerdp to use @rpath for Homebrew deps..." + patch_one_file "$FREERDP_BIN" + + # Ad-hoc sign so dyld doesn’t complain + log "Ad-hoc codesigning bundled libs and app..." + if [ -d "$LIB_DIR" ]; then + find "$LIB_DIR" -type f -name "*.dylib" -exec codesign --force --timestamp=none -s - {} \; + fi + if [ -d "$X11_DIR" ]; then + find "$X11_DIR" -type f -name "*.dylib" -exec codesign --force --timestamp=none -s - {} \; + fi + [ -f "$FREERDP_BIN" ] && codesign --force --timestamp=none -s - "$FREERDP_BIN" + codesign --force --deep --timestamp=none -s - "dist/$NAME.app" + + log "Verify xfreerdp linkage (should show @rpath -> ../lib and ../x11):" + otool -L "$FREERDP_BIN" | sed 's/^/ /' +else + log "Moving the executable to the $FINAL_DIR directory..." + mv "dist/$NAME" "$FINAL_DIR/" +fi + +# Verify the vendored version (macOS and linux) +expect_major="3" # adjust if you vendor 2.x +if [ -x "${FREERDP_BIN:-}" ]; then + vend_ver="$("$FREERDP_BIN" +version 2>/dev/null | head -n1 | grep -Eo '[0-9]+\.[0-9]+(\.[0-9]+)?' || true)" + if [ -z "$vend_ver" ]; then + log "WARN: Could not detect vendored FreeRDP version from +version" + else + vmaj="${vend_ver%%.*}" + if [ "$vmaj" != "$expect_major" ]; then + log "ERROR: Vendored FreeRDP major version is $vmaj, expected $expect_major" + exit 1 + fi + log "Vendored FreeRDP version: $vend_ver" + fi +fi + +# On Linux, fail fast if vendored xfreerdp has unresolved deps +if [ "$OS" = "linux" ] && [ -x "${FREERDP_BIN:-}" ]; then + if command -v ldd >/dev/null 2>&1; then + if ldd "$FREERDP_BIN" | grep -q "not found"; then + log "ERROR: Missing shared libraries for vendored xfreerdp:" + ldd "$FREERDP_BIN" | grep "not found" || true + exit 1 + fi + else + log "WARN: ldd not available; skipping shared-library check." + fi +fi + +# Move the built application or executable to the appropriate directory +if [ "$OS" == "macos" ]; then + log "Moving the .app bundle to the $FINAL_DIR directory..." + mv "dist/$NAME.app" "$FINAL_DIR/" + + # Create a DMG image + log "Creating a DMG image for macOS..." + DMG_NAME="$FINAL_DIR/$NAME.dmg" + hdiutil create "$DMG_NAME" -volname "$NAME" -srcfolder "$FINAL_DIR/$NAME.app" -ov -format UDZO + + log "DMG image created at $DMG_NAME" +fi + +# Cleanup: Remove the leftover dist/$NAME directory on macOS +if [ -d "dist/$NAME" ]; then + log "Cleaning up the dist directory..." + rm -rf "dist/$NAME" +fi + +log "Build completed successfully." + +# Deactivate the virtual environment +deactivate diff --git a/src/core b/src/core new file mode 160000 index 0000000..9e9802e --- /dev/null +++ b/src/core @@ -0,0 +1 @@ +Subproject commit 9e9802ec31981ac75d6566759c1b60b1eca803ad diff --git a/src/icons/icon.icns b/src/icons/icon.icns new file mode 100644 index 0000000000000000000000000000000000000000..c9892003bcb644ed2ca6a34697192f41c613cb73 GIT binary patch literal 205240 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≧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

!!5j-9{)-+`0E~JJoX@hK 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�v=g;bNHTD99VMrn7_NRR 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 literal 0 HcmV?d00001 diff --git a/src/icons/icon.svg b/src/icons/icon.svg new file mode 100644 index 0000000..3be12c0 --- /dev/null +++ b/src/icons/icon.svg @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main.py b/src/main.py new file mode 100755 index 0000000..fca3c10 --- /dev/null +++ b/src/main.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python3 +# src/main.py + +import sys + +from core.application import Application +from core.cli import CommandLine +from replicator.replicator import Replicator + +# --------------------------------------------------------------------------- +# Customization and start of the application +# --------------------------------------------------------------------------- + +name = "Replicator" + +# --------------------------------------------------------------------------- +# Helpers +# --------------------------------------------------------------------------- + +def has_option(): + return any(arg.startswith('--') for arg in sys.argv) + +# --------------------------------------------------------------------------- +# Start of the application +# --------------------------------------------------------------------------- + +def start_app(): + app = Application(name,sys.argv) + + # Create main window and register it with Application + win = Replicator() + app.set_mainWindow(win) + + # All other code gets app via QApplication.instance() + sys.exit(app.exec_()) + +def start_cli(): + cli = CommandLine(name,sys.argv) + + # All other code gets app via QApplication.instance() + sys.exit(cli.exec()) + +# --------------------------------------------------------------------------- +# Main entry point +# --------------------------------------------------------------------------- + +if __name__ == "__main__": + if has_option(): + start_cli() + else: + start_app() diff --git a/src/replicator/replicator.py b/src/replicator/replicator.py new file mode 100644 index 0000000..95caa31 --- /dev/null +++ b/src/replicator/replicator.py @@ -0,0 +1,507 @@ +#!/usr/bin/env python3 +# src/replicator/replicator.py + +from __future__ import annotations + +from typing import Optional, Any, Dict, List + +# Add datetime import for lastRun/lastResult +from datetime import datetime, timezone + +from PyQt5.QtCore import Qt +from PyQt5.QtGui import QIcon, QPixmap +from PyQt5.QtWidgets import ( + QApplication, + QMainWindow, + QWidget, + QVBoxLayout, + QHBoxLayout, + QLabel, + QPushButton, + QTableWidget, + QTableWidgetItem, + QHeaderView, + QDialog, + QFormLayout, + QLineEdit, + QCheckBox, + QComboBox, + QSpinBox, +) + +try: + from core.helper import Helper + from core.configuration import Configuration + from core.log import Log + from core.ui import MsgBox, Form + from core.filesystem.filesystem import FileSystem +except ImportError: + from helper import Helper + from configuration import Configuration + from log import Log + from ui import MsgBox, Form + from filesystem.filesystem import FileSystem + + +class JobDialog(QDialog): + def __init__(self, parent=None, job: Optional[Dict[str, Any]] = None): + super().__init__(parent) + self.setWindowTitle("Replication Job") + self.setModal(True) + + job = job or {} + self._original_job = job + + layout = QVBoxLayout(self) + form = QFormLayout() + layout.addLayout(form) + + self.name = QLineEdit(job.get("name", "")) + self.source = QLineEdit(job.get("source", "")) + self.target = QLineEdit(job.get("target", "")) + self.mode = QComboBox() + self.mode.addItems(["mirror"]) + mode_val = job.get("mode", "mirror") + idx = self.mode.findText(mode_val) + if idx >= 0: + self.mode.setCurrentIndex(idx) + self.direction = QComboBox() + self.direction.addItems(["unidirectional", "bidirectional"]) + direction_val = job.get("direction", "unidirectional") + idx = self.direction.findText(direction_val) + if idx >= 0: + self.direction.setCurrentIndex(idx) + self.allow_deletion = QCheckBox() + self.allow_deletion.setChecked(bool(job.get("allowDeletion", False))) + self.preserve_metadata = QCheckBox() + self.preserve_metadata.setChecked(bool(job.get("preserveMetadata", True))) + + form.addRow("Name", self.name) + form.addRow("Source", self.source) + form.addRow("Target", self.target) + form.addRow("Mode", self.mode) + form.addRow("Direction", self.direction) + form.addRow("Allow deletion", self.allow_deletion) + form.addRow("Preserve metadata", self.preserve_metadata) + + btn_row = QHBoxLayout() + btn_row.addStretch(1) + + cancel_btn = QPushButton("Cancel") + ok_btn = QPushButton("Save") + cancel_btn.clicked.connect(self.reject) + ok_btn.clicked.connect(self._on_ok) + + btn_row.addWidget(cancel_btn) + btn_row.addWidget(ok_btn) + layout.addLayout(btn_row) + + def _on_ok(self): + if not self.name.text().strip(): + MsgBox.show(self, "Job", "Name is required.", icon="warning") + return + if not self.source.text().strip(): + MsgBox.show(self, "Job", "Source is required.", icon="warning") + return + if not self.target.text().strip(): + MsgBox.show(self, "Job", "Target is required.", icon="warning") + return + self.accept() + + def value(self) -> Dict[str, Any]: + val: Dict[str, Any] = { + "name": self.name.text().strip(), + "source": self.source.text().strip(), + "target": self.target.text().strip(), + "allowDeletion": bool(self.allow_deletion.isChecked()), + "preserveMetadata": bool(self.preserve_metadata.isChecked()), + "mode": self.mode.currentText(), + "direction": self.direction.currentText(), + } + # Preserve schedule fields if they existed previously + if self._original_job and isinstance(self._original_job, dict): + sched = self._original_job.get("schedule") + if sched is not None: + val["schedule"] = sched + return val + + +class ScheduleDialog(QDialog): + def __init__(self, parent=None, job: Optional[Dict[str, Any]] = None): + super().__init__(parent) + self.setWindowTitle("Schedule") + self.setModal(True) + + job = job or {} + schedule = job.get("schedule", {}) + + layout = QVBoxLayout(self) + form = QFormLayout() + layout.addLayout(form) + + self.enabled = QCheckBox() + self.enabled.setChecked(bool(schedule.get("enabled", False))) + self.every_minutes = QSpinBox() + self.every_minutes.setRange(1, 10080) + self.every_minutes.setValue(int(schedule.get("everyMinutes", 60))) + + form.addRow("Enabled", self.enabled) + form.addRow("Every minutes", self.every_minutes) + + btn_row = QHBoxLayout() + btn_row.addStretch(1) + + cancel_btn = QPushButton("Cancel") + ok_btn = QPushButton("Save") + cancel_btn.clicked.connect(self.reject) + ok_btn.clicked.connect(self._on_ok) + + btn_row.addWidget(cancel_btn) + btn_row.addWidget(ok_btn) + layout.addLayout(btn_row) + + def _on_ok(self): + if self.enabled.isChecked() and self.every_minutes.value() < 1: + MsgBox.show(self, "Schedule", "Every minutes must be at least 1 if enabled.", icon="warning") + return + self.accept() + + def value(self) -> Dict[str, Any]: + return { + "enabled": bool(self.enabled.isChecked()), + "everyMinutes": int(self.every_minutes.value()), + } + + +class Replicator(QMainWindow): + """ + Replicator UI + CLI entrypoint. + Jobs are stored in configuration under: replicator.jobs (list of dicts). + """ + + def __init__( + self, + helper: Optional[Helper] = None, + configuration: Optional[Configuration] = None, + logger: Optional[Log] = None, + ): + super().__init__() + + self._app = QApplication.instance() + if self._app is None: + raise RuntimeError("Replicator must be created after QApplication/Application.") + + helper = helper or getattr(self._app, "helper", None) + configuration = configuration or getattr(self._app, "configuration", None) + logger = logger or getattr(self._app, "logger", None) + + if helper is None or configuration is None: + raise RuntimeError("Replicator requires corePY Helper + Configuration.") + + self._helper: Helper = helper + self._configuration: Configuration = configuration + self._logger: Optional[Log] = logger + + self._fs = FileSystem(helper=self._helper, logger=self._logger) + + # Ensure defaults exist + if self._configuration.get("replicator.jobs") is None: + self._configuration.add("replicator.jobs", [], "json", label="Jobs") + self._configuration.save() + + self._jobs: List[Dict[str, Any]] = [] + self._table: Optional[QTableWidget] = None + + # ------------------------------------------------------------------ + # CLI integration + # ------------------------------------------------------------------ + + def cli(self, cli: QApplication = None) -> None: + """ + Called by corePY CommandLine when running in CLI mode. + """ + if cli is None: + return + cli.add("run", "Run all replication jobs.", self.run) + + # ------------------------------------------------------------------ + # UI + # ------------------------------------------------------------------ + + def show(self): + self.init() + super().show() + + def init(self): + self.setWindowTitle(getattr(self._app, "name", "Replicator")) + self.setObjectName("Replicator") + + central = QWidget(self) + self.setCentralWidget(central) + root = QVBoxLayout(central) + root.setContentsMargins(16, 16, 16, 16) + root.setSpacing(12) + + # Logo (top, centered) + logo = QLabel() + logo.setAlignment(Qt.AlignCenter) + + # Use app icon/logo if you have one; otherwise harmless + # Adjust path to wherever you store icons in Replicator + candidate = self._helper.get_path("icons/icon.png") or self._helper.get_path("core/icons/info.svg") + if candidate and self._helper.file_exists(candidate) and candidate.lower().endswith(".png"): + pm = QPixmap(candidate) + if not pm.isNull(): + logo.setPixmap(pm.scaled(96, 96, Qt.KeepAspectRatio, Qt.SmoothTransformation)) + else: + logo.setText("Replicator") + logo.setStyleSheet("font-size: 22px; font-weight: 600;") + + root.addWidget(logo) + + # Table + self._table = QTableWidget(0, 10, self) + self._table.setHorizontalHeaderLabels([ + "Name", "Source", "Target", "Mode", "Direction", "Delete", "Metadata", "Schedule", "Last run", "Result" + ]) + self._table.horizontalHeader().setSectionResizeMode(0, QHeaderView.ResizeToContents) + self._table.horizontalHeader().setSectionResizeMode(1, QHeaderView.Stretch) + self._table.horizontalHeader().setSectionResizeMode(2, QHeaderView.Stretch) + self._table.horizontalHeader().setSectionResizeMode(3, QHeaderView.ResizeToContents) + self._table.horizontalHeader().setSectionResizeMode(4, QHeaderView.ResizeToContents) + self._table.horizontalHeader().setSectionResizeMode(5, QHeaderView.ResizeToContents) + self._table.horizontalHeader().setSectionResizeMode(6, QHeaderView.ResizeToContents) + self._table.horizontalHeader().setSectionResizeMode(7, QHeaderView.ResizeToContents) + self._table.horizontalHeader().setSectionResizeMode(8, QHeaderView.ResizeToContents) + self._table.horizontalHeader().setSectionResizeMode(9, QHeaderView.ResizeToContents) + self._table.setSelectionBehavior(QTableWidget.SelectRows) + self._table.setEditTriggers(QTableWidget.NoEditTriggers) + + root.addWidget(self._table) + + # Buttons (icon only using Form.button) + btn_row = QHBoxLayout() + btn_row.addStretch(1) + + add_btn = Form.button(label="", icon="plus-circle", action=self._add_job) + add_btn.setToolTip("Add job") + edit_btn = Form.button(label="", icon="pencil-square", action=self._edit_job) + edit_btn.setToolTip("Edit job") + dup_btn = Form.button(label="", icon="files", action=self._duplicate_job) + dup_btn.setToolTip("Duplicate job") + del_btn = Form.button(label="", icon="trash", action=self._delete_job) + del_btn.setToolTip("Delete job") + schedule_btn = Form.button(label="", icon="calendar-event", action=self._edit_schedule) + schedule_btn.setToolTip("Schedule") + config_btn = Form.button(label="", icon="gear-fill", action=self._configuration.show) + config_btn.setToolTip("Configurator") + run_btn = Form.button(label="", icon="play-fill", action=lambda: self._run_with_ui_feedback()) + run_btn.setToolTip("Run now") + + btn_row.addWidget(add_btn) + btn_row.addWidget(edit_btn) + btn_row.addWidget(dup_btn) + btn_row.addWidget(del_btn) + btn_row.addWidget(schedule_btn) + btn_row.addWidget(config_btn) + btn_row.addWidget(run_btn) + + root.addLayout(btn_row) + + self._reload_jobs() + + def _selected_index(self) -> int: + if not self._table: + return -1 + rows = self._table.selectionModel().selectedRows() + if not rows: + return -1 + return rows[0].row() + + def _reload_jobs(self): + self._jobs = self._configuration.get("replicator.jobs", []) or [] + self._refresh_table() + + def _save_jobs(self): + self._configuration.set("replicator.jobs", self._jobs) + self._configuration.save() + + def _refresh_table(self): + if not self._table: + return + self._table.setRowCount(0) + + for job in self._jobs: + r = self._table.rowCount() + self._table.insertRow(r) + + def _it(v: Any) -> QTableWidgetItem: + item = QTableWidgetItem(str(v) if v is not None else "") + item.setFlags(item.flags() ^ Qt.ItemIsEditable) + return item + + self._table.setItem(r, 0, _it(job.get("name", ""))) + self._table.setItem(r, 1, _it(job.get("source", ""))) + self._table.setItem(r, 2, _it(job.get("target", ""))) + self._table.setItem(r, 3, _it(job.get("mode", "mirror"))) + self._table.setItem(r, 4, _it(job.get("direction", "unidirectional"))) + self._table.setItem(r, 5, _it("Yes" if job.get("allowDeletion") else "No")) + self._table.setItem(r, 6, _it("Yes" if job.get("preserveMetadata", True) else "No")) + sched = job.get("schedule") + if not sched or not sched.get("enabled", False): + sched_str = "Off" + else: + sched_str = f"Every {sched.get('everyMinutes', 60)} min" + self._table.setItem(r, 7, _it(sched_str)) + # Last run (column 8) + last_run = job.get("lastRun") + if last_run: + last_run_str = last_run + else: + last_run_str = "Never" + self._table.setItem(r, 8, _it(last_run_str)) + # Last result (column 9) + last_result = job.get("lastResult", "") + self._table.setItem(r, 9, _it(last_result)) + + def _add_job(self): + dlg = JobDialog(self) + if dlg.exec_() == QDialog.Accepted: + self._jobs.append(dlg.value()) + self._save_jobs() + self._refresh_table() + + def _edit_job(self): + idx = self._selected_index() + if idx < 0: + MsgBox.show(self, "Jobs", "Select a job to edit.", icon="info") + return + dlg = JobDialog(self, self._jobs[idx]) + if dlg.exec_() == QDialog.Accepted: + self._jobs[idx] = dlg.value() + self._save_jobs() + self._refresh_table() + + def _duplicate_job(self): + idx = self._selected_index() + if idx < 0: + MsgBox.show(self, "Jobs", "Select a job to duplicate.", icon="info") + return + orig_job = self._jobs[idx] + new_job = dict(orig_job) + # Deep copy schedule if present + if "schedule" in orig_job and isinstance(orig_job["schedule"], dict): + new_job["schedule"] = dict(orig_job["schedule"]) + name = new_job.get("name") + if name: + new_job["name"] = f"{name} (copy)" + else: + new_job["name"] = "Copy" + self._jobs.append(new_job) + self._save_jobs() + self._refresh_table() + # Select the new row + if self._table: + new_row = self._table.rowCount() - 1 + self._table.selectRow(new_row) + + def _delete_job(self): + idx = self._selected_index() + if idx < 0: + MsgBox.show(self, "Jobs", "Select a job to delete.", icon="info") + return + choice = MsgBox.show( + self, + title="Delete", + message=f"Delete job '{self._jobs[idx].get('name', '')}'?", + icon="question", + buttons=("Cancel", "Delete"), + default="Cancel", + ) + if choice == "Delete": + self._jobs.pop(idx) + self._save_jobs() + self._refresh_table() + + def _edit_schedule(self): + idx = self._selected_index() + if idx < 0: + MsgBox.show(self, "Jobs", "Select a job to edit schedule.", icon="info") + return + dlg = ScheduleDialog(self, job=self._jobs[idx]) + if dlg.exec_() == QDialog.Accepted: + self._jobs[idx]["schedule"] = dlg.value() + self._save_jobs() + self._refresh_table() + + def _run_with_ui_feedback(self): + ok = self.run() + MsgBox.show( + self, + title="Replicator", + message="Replication completed successfully." if ok else "Replication finished with errors.", + icon="info" if ok else "warning", + ) + + # ------------------------------------------------------------------ + # Execution + # ------------------------------------------------------------------ + + def run(self) -> bool: + """ + Run all configured jobs. + Returns True if all jobs succeeded. + """ + jobs = self._configuration.get("replicator.jobs", []) or [] + if not jobs: + self._log("[Replicator] No jobs configured.", level="warning") + return False + + all_ok = True + for job in jobs: + ok = self._run_job(job) + all_ok = all_ok and ok + + return all_ok + + def _run_job(self, job: Dict[str, Any]) -> bool: + name = job.get("name") or "Unnamed" + src = job.get("source") or "" + dst = job.get("target") or "" + allow_deletion = bool(job.get("allowDeletion", False)) + preserve_metadata = bool(job.get("preserveMetadata", True)) + mode = job.get("mode", "mirror") + direction = job.get("direction", "unidirectional") + schedule = job.get("schedule", {}) + + if not src or not dst: + self._log(f"[Replicator] Job '{name}' invalid: missing source/target.", level="error") + return False + + sched_str = "" + if schedule.get("enabled", False): + sched_str = f", schedule=Every {schedule.get('everyMinutes', 60)} min" + self._log(f"[Replicator] Running job '{name}': {src} -> {dst} (mode={mode}, direction={direction}, delete={allow_deletion}, meta={preserve_metadata}{sched_str})") + + ok = self._fs.copy( + src, + dst, + preserve_metadata=preserve_metadata, + allow_deletion=allow_deletion, + ) + + # Persist lastRun and lastResult, refresh UI, save + job["lastRun"] = datetime.now(timezone.utc).isoformat() + job["lastResult"] = "ok" if ok else "fail" + self._save_jobs() + if self._table: + self._refresh_table() + + self._log(f"[Replicator] Job '{name}' result: {'OK' if ok else 'FAIL'} (lastResult={job['lastResult']})", level="info" if ok else "warning") + return ok + + def _log(self, msg: str, level: str = "info", channel: str = "replicator") -> None: + if self._logger is not None and hasattr(self._logger, "append"): + self._logger.append(msg, level=level, channel=channel) # type: ignore[call-arg] + else: + print(msg) diff --git a/update.sh b/update.sh new file mode 100755 index 0000000..86308c9 --- /dev/null +++ b/update.sh @@ -0,0 +1,7 @@ +#!/bin/sh + +BRANCH=$(git config -f .gitmodules submodule.src/core.branch) +cd src/core && git checkout $BRANCH && git pull && cd ../.. +git add src/core +git commit -m "General: Update submodule corePY" || echo "No changes to commit" +git push From d3627f6169378de3f43bd3633fe549b85988b735 Mon Sep 17 00:00:00 2001 From: Louis Ouellet Date: Fri, 9 Jan 2026 16:25:19 -0500 Subject: [PATCH 02/51] General: Made several visual changes in the stylesheet to make the table more consistent and the button's behaviours more related. --- src/core | 2 +- src/replicator/replicator.py | 94 +++++++++++++++++++++++------------- 2 files changed, 61 insertions(+), 35 deletions(-) diff --git a/src/core b/src/core index 9e9802e..32f49cf 160000 --- a/src/core +++ b/src/core @@ -1 +1 @@ -Subproject commit 9e9802ec31981ac75d6566759c1b60b1eca803ad +Subproject commit 32f49cf1d49c7be0c55b8ab4367785e18a03ee98 diff --git a/src/replicator/replicator.py b/src/replicator/replicator.py index 95caa31..c0a3da1 100644 --- a/src/replicator/replicator.py +++ b/src/replicator/replicator.py @@ -235,6 +235,8 @@ def show(self): def init(self): self.setWindowTitle(getattr(self._app, "name", "Replicator")) self.setObjectName("Replicator") + # Ensure minimum window width for table visibility + self.setMinimumWidth(800) central = QWidget(self) self.setCentralWidget(central) @@ -259,56 +261,80 @@ def init(self): root.addWidget(logo) + # --- Actions row (top, after logo) --- + actions_row = QHBoxLayout() + # No stretch at start; left-aligned by default + self._add_btn = Form.button(label="", icon="plus-circle", action=self._add_job) + self._add_btn.setToolTip("Add job") + self._edit_btn = Form.button(label="", icon="pencil-square", action=self._edit_job) + self._edit_btn.setToolTip("Edit job") + self._edit_btn.setVisible(False) + self._dup_btn = Form.button(label="", icon="files", action=self._duplicate_job) + self._dup_btn.setToolTip("Duplicate job") + self._dup_btn.setVisible(False) + self._del_btn = Form.button(label="", icon="trash", action=self._delete_job) + self._del_btn.setToolTip("Delete job") + self._del_btn.setVisible(False) + self._schedule_btn = Form.button(label="", icon="calendar-event", action=self._edit_schedule) + self._schedule_btn.setToolTip("Schedule") + self._schedule_btn.setVisible(False) + actions_row.addWidget(self._add_btn) + actions_row.addWidget(self._edit_btn) + actions_row.addWidget(self._dup_btn) + actions_row.addWidget(self._del_btn) + actions_row.addWidget(self._schedule_btn) + actions_row.addStretch(1) + root.addLayout(actions_row) + # Table self._table = QTableWidget(0, 10, self) self._table.setHorizontalHeaderLabels([ "Name", "Source", "Target", "Mode", "Direction", "Delete", "Metadata", "Schedule", "Last run", "Result" ]) - self._table.horizontalHeader().setSectionResizeMode(0, QHeaderView.ResizeToContents) - self._table.horizontalHeader().setSectionResizeMode(1, QHeaderView.Stretch) - self._table.horizontalHeader().setSectionResizeMode(2, QHeaderView.Stretch) - self._table.horizontalHeader().setSectionResizeMode(3, QHeaderView.ResizeToContents) - self._table.horizontalHeader().setSectionResizeMode(4, QHeaderView.ResizeToContents) - self._table.horizontalHeader().setSectionResizeMode(5, QHeaderView.ResizeToContents) - self._table.horizontalHeader().setSectionResizeMode(6, QHeaderView.ResizeToContents) - self._table.horizontalHeader().setSectionResizeMode(7, QHeaderView.ResizeToContents) - self._table.horizontalHeader().setSectionResizeMode(8, QHeaderView.ResizeToContents) - self._table.horizontalHeader().setSectionResizeMode(9, QHeaderView.ResizeToContents) self._table.setSelectionBehavior(QTableWidget.SelectRows) self._table.setEditTriggers(QTableWidget.NoEditTriggers) - root.addWidget(self._table) + # Hide row numbers + self._table.verticalHeader().setVisible(False) - # Buttons (icon only using Form.button) - btn_row = QHBoxLayout() - btn_row.addStretch(1) + # Connect selection change to update visibility of action buttons + self._table.selectionModel().selectionChanged.connect(self._on_selection_changed) + + # Ensure columns never shrink below their content (use scrollbar for overflow) + header = self._table.horizontalHeader() + for i in range(self._table.columnCount()): + header.setSectionResizeMode(i, QHeaderView.ResizeToContents) - add_btn = Form.button(label="", icon="plus-circle", action=self._add_job) - add_btn.setToolTip("Add job") - edit_btn = Form.button(label="", icon="pencil-square", action=self._edit_job) - edit_btn.setToolTip("Edit job") - dup_btn = Form.button(label="", icon="files", action=self._duplicate_job) - dup_btn.setToolTip("Duplicate job") - del_btn = Form.button(label="", icon="trash", action=self._delete_job) - del_btn.setToolTip("Delete job") - schedule_btn = Form.button(label="", icon="calendar-event", action=self._edit_schedule) - schedule_btn.setToolTip("Schedule") + # Keep horizontal scrollbar available for overflow + header.setStretchLastSection(False) + + root.addWidget(self._table) + + # --- Bottom row: Configurator and Run now (right aligned) --- + bottom_row = QHBoxLayout() + bottom_row.addStretch(1) config_btn = Form.button(label="", icon="gear-fill", action=self._configuration.show) config_btn.setToolTip("Configurator") run_btn = Form.button(label="", icon="play-fill", action=lambda: self._run_with_ui_feedback()) run_btn.setToolTip("Run now") - - btn_row.addWidget(add_btn) - btn_row.addWidget(edit_btn) - btn_row.addWidget(dup_btn) - btn_row.addWidget(del_btn) - btn_row.addWidget(schedule_btn) - btn_row.addWidget(config_btn) - btn_row.addWidget(run_btn) - - root.addLayout(btn_row) + bottom_row.addWidget(config_btn) + bottom_row.addWidget(run_btn) + root.addLayout(bottom_row) self._reload_jobs() + self._on_selection_changed() + + + def _on_selection_changed(self, *_args): + has = self._selected_index() >= 0 + if hasattr(self, "_edit_btn"): + self._edit_btn.setVisible(has) + if hasattr(self, "_dup_btn"): + self._dup_btn.setVisible(has) + if hasattr(self, "_del_btn"): + self._del_btn.setVisible(has) + if hasattr(self, "_schedule_btn"): + self._schedule_btn.setVisible(has) def _selected_index(self) -> int: if not self._table: From 7953a88c903d532cb121bdf9db36c93de99c095f Mon Sep 17 00:00:00 2001 From: Louis Ouellet Date: Fri, 9 Jan 2026 20:21:24 -0500 Subject: [PATCH 03/51] General: Made several visual changes in the stylesheet to make the table more consistent and the button's behaviours more related. --- src/replicator/replicator.py | 665 +++++++++++++++++++++++++++++++++-- 1 file changed, 636 insertions(+), 29 deletions(-) diff --git a/src/replicator/replicator.py b/src/replicator/replicator.py index c0a3da1..4a69fc1 100644 --- a/src/replicator/replicator.py +++ b/src/replicator/replicator.py @@ -27,6 +27,11 @@ QCheckBox, QComboBox, QSpinBox, + QStackedWidget, + QTextEdit, + QFrame, + QSizePolicy, + QLayout, ) try: @@ -43,86 +48,653 @@ from filesystem.filesystem import FileSystem + class JobDialog(QDialog): def __init__(self, parent=None, job: Optional[Dict[str, Any]] = None): super().__init__(parent) self.setWindowTitle("Replication Job") self.setModal(True) + self.setFixedHeight(400) + self.setMinimumHeight(400) + self.setMinimumWidth(1200) job = job or {} self._original_job = job layout = QVBoxLayout(self) - form = QFormLayout() - layout.addLayout(form) - + layout.setAlignment(Qt.AlignTop) + layout.setSizeConstraint(QLayout.SetMinimumSize) + + # ------------------------------ + # Name row (full width) + # ------------------------------ + name_row = QHBoxLayout() + name_row.addWidget(QLabel("Name")) self.name = QLineEdit(job.get("name", "")) - self.source = QLineEdit(job.get("source", "")) - self.target = QLineEdit(job.get("target", "")) + name_row.addWidget(self.name, 1) + layout.addLayout(name_row) + + # Helper to get endpoint dict from job or fallback + def _get_endpoint(key_endpoint: str, key_str: str, default_type: str = "local") -> Dict[str, Any]: + ep = job.get(key_endpoint) + if isinstance(ep, dict): + return dict(ep) + return { + "type": default_type, + "location": job.get(key_str, ""), + "auth": {}, + } + + self._source_ep = _get_endpoint("sourceEndpoint", "source", "local") + self._target_ep = _get_endpoint("targetEndpoint", "target", "local") + + # ------------------------------ + # Two-column area: Source / Destination + # ------------------------------ + cols = QHBoxLayout() + cols.setSpacing(20) + # Keep endpoint panels compact (avoid vertical stretching) + cols.setAlignment(Qt.AlignTop) + + # Bordered containers for better visual separation + src_frame = QFrame() + src_frame.setObjectName("EndpointFrame") + src_frame.setFrameShape(QFrame.NoFrame) + src_frame.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Maximum) + src_frame.setMinimumHeight(0) + src_frame.setStyleSheet( + "#EndpointFrame { border: 1px solid rgba(255,255,255,0.12); border-radius: 8px; padding: 10px; }" + ) + src_col = QVBoxLayout(src_frame) + src_col.setContentsMargins(10, 10, 10, 10) + src_col.setSpacing(8) + src_col.setAlignment(Qt.AlignTop) + + dst_frame = QFrame() + dst_frame.setObjectName("EndpointFrame") + dst_frame.setFrameShape(QFrame.NoFrame) + dst_frame.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Maximum) + dst_frame.setMinimumHeight(0) + dst_frame.setStyleSheet( + "#EndpointFrame { border: 1px solid rgba(255,255,255,0.12); border-radius: 8px; padding: 10px; }" + ) + dst_col = QVBoxLayout(dst_frame) + dst_col.setContentsMargins(10, 10, 10, 10) + dst_col.setSpacing(8) + dst_col.setAlignment(Qt.AlignTop) + + src_title = QLabel("Source") + src_title.setStyleSheet("font-weight: 600;") + dst_title = QLabel("Destination") + dst_title.setStyleSheet("font-weight: 600;") + + src_col.addWidget(src_title) + dst_col.addWidget(dst_title) + + ( + self._source_type_combo, + self._source_location_edit, + self._source_port_spin, + self._source_endpoint_row, + self._source_auth_widget, + self._source_auth_widgets, + ) = self._build_endpoint(existing=self._source_ep) + + ( + self._target_type_combo, + self._target_location_edit, + self._target_port_spin, + self._target_endpoint_row, + self._target_auth_widget, + self._target_auth_widgets, + ) = self._build_endpoint(existing=self._target_ep) + + # Endpoint row: Type + Location (+ Port for FTP/SSH) + src_col.addWidget(self._source_endpoint_row) + src_col.addWidget(self._source_auth_widget) + + dst_col.addWidget(self._target_endpoint_row) + dst_col.addWidget(self._target_auth_widget) + + cols.addWidget(src_frame, 1) + cols.addWidget(dst_frame, 1) + layout.addLayout(cols) + + # ------------------------------ + # Mode + Direction on same line + # ------------------------------ self.mode = QComboBox() self.mode.addItems(["mirror"]) mode_val = job.get("mode", "mirror") idx = self.mode.findText(mode_val) if idx >= 0: self.mode.setCurrentIndex(idx) + self.direction = QComboBox() self.direction.addItems(["unidirectional", "bidirectional"]) direction_val = job.get("direction", "unidirectional") idx = self.direction.findText(direction_val) if idx >= 0: self.direction.setCurrentIndex(idx) - self.allow_deletion = QCheckBox() + + # Mode + Direction on same line, each taking half width + mode_dir_row = QHBoxLayout() + + mode_wrap = QWidget() + mode_wrap.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) + mode_lay = QHBoxLayout(mode_wrap) + mode_lay.setContentsMargins(0, 0, 0, 0) + mode_lay.setSpacing(8) + mode_lay.addWidget(QLabel("Mode")) + mode_lay.addWidget(self.mode, 1) + + dir_wrap = QWidget() + dir_wrap.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) + dir_lay = QHBoxLayout(dir_wrap) + dir_lay.setContentsMargins(0, 0, 0, 0) + dir_lay.setSpacing(8) + dir_lay.addWidget(QLabel("Direction")) + dir_lay.addWidget(self.direction, 1) + + self.mode.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) + self.direction.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) + + mode_dir_row.addWidget(mode_wrap, 1) + mode_dir_row.addWidget(dir_wrap, 1) + layout.addLayout(mode_dir_row) + + # ------------------------------ + # Other options (kept simple) + # ------------------------------ + opts_row = QHBoxLayout() + self.allow_deletion = QCheckBox("Allow deletion") self.allow_deletion.setChecked(bool(job.get("allowDeletion", False))) - self.preserve_metadata = QCheckBox() + self.preserve_metadata = QCheckBox("Preserve metadata") self.preserve_metadata.setChecked(bool(job.get("preserveMetadata", True))) - - form.addRow("Name", self.name) - form.addRow("Source", self.source) - form.addRow("Target", self.target) - form.addRow("Mode", self.mode) - form.addRow("Direction", self.direction) - form.addRow("Allow deletion", self.allow_deletion) - form.addRow("Preserve metadata", self.preserve_metadata) - + opts_row.addWidget(self.allow_deletion) + opts_row.addSpacing(16) + opts_row.addWidget(self.preserve_metadata) + opts_row.addStretch(1) + layout.addLayout(opts_row) + + # ------------------------------ + # Buttons + # ------------------------------ btn_row = QHBoxLayout() btn_row.addStretch(1) - cancel_btn = QPushButton("Cancel") ok_btn = QPushButton("Save") cancel_btn.clicked.connect(self.reject) ok_btn.clicked.connect(self._on_ok) - btn_row.addWidget(cancel_btn) btn_row.addWidget(ok_btn) layout.addLayout(btn_row) + # Wire type changes + self._source_type_combo.currentIndexChanged.connect(lambda _i: self._on_type_changed( + self._source_type_combo, + self._source_location_edit, + self._source_port_spin, + self._source_auth_widget, + self._source_auth_widgets, + )) + self._target_type_combo.currentIndexChanged.connect(lambda _i: self._on_type_changed( + self._target_type_combo, + self._target_location_edit, + self._target_port_spin, + self._target_auth_widget, + self._target_auth_widgets, + )) + + # Trigger initial state + self._on_type_changed( + self._source_type_combo, + self._source_location_edit, + self._source_port_spin, + self._source_auth_widget, + self._source_auth_widgets, + initial=True, + ) + self._on_type_changed( + self._target_type_combo, + self._target_location_edit, + self._target_port_spin, + self._target_auth_widget, + self._target_auth_widgets, + initial=True, + ) + + # ------------------------------------------------------------------ + # Endpoint UI + # ------------------------------------------------------------------ + + def _build_endpoint(self, existing: Dict[str, Any]): + """Build endpoint widgets. + + Returns: + (type_combo, location_edit, port_spin, endpoint_row_widget, auth_widget, widgets_dict) + """ + # Top row: [Type] [Location] [Port (FTP/SSH)] + type_combo = QComboBox() + type_combo.addItems(["local", "smb", "ftp", "ssh"]) + idx = type_combo.findText(existing.get("type", "local")) + if idx >= 0: + type_combo.setCurrentIndex(idx) + type_combo.setFixedWidth(110) + # Allow _on_type_changed to access original config + type_combo.setProperty("existing_type", existing.get("type", "local")) + type_combo.setProperty("existing_auth", existing.get("auth", {}) or {}) + + location_edit = QLineEdit(existing.get("location", "")) + + port_spin = QSpinBox() + port_spin.setRange(1, 65535) + port_spin.setFixedWidth(110) + # Default ports (used when the type is FTP/SSH) + existing_type = existing.get("type", "local") + existing_auth = existing.get("auth", {}) or {} + if existing_type == "ftp": + port_spin.setValue(int(existing_auth.get("port", 21) or 21)) + elif existing_type == "ssh": + port_spin.setValue(int(existing_auth.get("port", 22) or 22)) + else: + # Keep a sane default value even when hidden + port_spin.setValue(21) + + # Endpoint row widget with label: [Label] [Type] [Location] [Port] + endpoint_fields = QWidget() + fields_lay = QHBoxLayout(endpoint_fields) + fields_lay.setContentsMargins(0, 0, 0, 0) + fields_lay.setSpacing(8) + fields_lay.addWidget(type_combo) + fields_lay.addWidget(location_edit, 1) + fields_lay.addWidget(port_spin) + + endpoint_row = QWidget() + endpoint_row.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) + endpoint_form = QFormLayout(endpoint_row) + endpoint_form.setContentsMargins(0, 0, 0, 0) + endpoint_form.setSpacing(6) + endpoint_form.addRow(endpoint_fields) + + # Auth widget (shown only when non-local) + # Use a dedicated sub-container that shrinks/grows with its visible children. + auth_widget = QFrame() + auth_widget.setFrameShape(QFrame.NoFrame) + auth_widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Maximum) + auth_widget.setMinimumHeight(0) + + auth_lay = QVBoxLayout(auth_widget) + auth_lay.setContentsMargins(0, 0, 0, 0) + auth_lay.setSpacing(6) + auth_lay.setAlignment(Qt.AlignTop) + + auth_title = QLabel("Authentication") + auth_title.setStyleSheet("font-weight: 600;") + + widgets: Dict[str, Any] = {} + + # SMB auth + smb_wrap = QWidget() + smb_wrap.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Maximum) + smb_form = QFormLayout(smb_wrap) + smb_form.setContentsMargins(0, 0, 0, 0) + smb_guest = QCheckBox("Login as Guest") + smb_user_lbl = QLabel("Username") + smb_username = QLineEdit() + smb_pass_lbl = QLabel("Password") + smb_password = QLineEdit() + smb_password.setEchoMode(QLineEdit.Password) + + smb_auth = existing.get("auth", {}) if existing.get("type") == "smb" else {} + smb_guest.setChecked(bool(smb_auth.get("guest", True))) # default guest ON + smb_username.setText(smb_auth.get("username", "")) + smb_password.setText(smb_auth.get("password", "")) + + smb_form.addRow(smb_guest) + smb_form.addRow(smb_user_lbl, smb_username) + smb_form.addRow(smb_pass_lbl, smb_password) + + def _smb_guest_update(): + guest = smb_guest.isChecked() + smb_user_lbl.setVisible(not guest) + smb_username.setVisible(not guest) + smb_pass_lbl.setVisible(not guest) + smb_password.setVisible(not guest) + + smb_guest.stateChanged.connect(_smb_guest_update) + _smb_guest_update() + + # FTP auth + ftp_wrap = QWidget() + ftp_wrap.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Maximum) + ftp_form = QFormLayout(ftp_wrap) + ftp_form.setContentsMargins(0, 0, 0, 0) + ftp_guest = QCheckBox("Login as Guest") + ftp_user_lbl = QLabel("Username") + ftp_username = QLineEdit() + ftp_pass_lbl = QLabel("Password") + ftp_password = QLineEdit() + ftp_password.setEchoMode(QLineEdit.Password) + + ftp_auth = existing.get("auth", {}) if existing.get("type") == "ftp" else {} + ftp_guest.setChecked(bool(ftp_auth.get("guest", True))) # default guest ON + ftp_username.setText(ftp_auth.get("username", "")) + ftp_password.setText(ftp_auth.get("password", "")) + + ftp_form.addRow(ftp_guest) + ftp_form.addRow(ftp_user_lbl, ftp_username) + ftp_form.addRow(ftp_pass_lbl, ftp_password) + + def _ftp_guest_update(): + guest = ftp_guest.isChecked() + ftp_user_lbl.setVisible(not guest) + ftp_username.setVisible(not guest) + ftp_pass_lbl.setVisible(not guest) + ftp_password.setVisible(not guest) + + ftp_guest.stateChanged.connect(_ftp_guest_update) + _ftp_guest_update() + + # SSH auth + ssh_wrap = QWidget() + ssh_wrap.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Maximum) + ssh_form = QFormLayout(ssh_wrap) + ssh_form.setContentsMargins(0, 0, 0, 0) + ssh_use_key = QCheckBox("Use SSH Key") + ssh_user_lbl = QLabel("Username") + ssh_username = QLineEdit() + ssh_pass_lbl = QLabel("Password") + ssh_password = QLineEdit() + ssh_password.setEchoMode(QLineEdit.Password) + ssh_key_lbl = QLabel("SSH Key") + ssh_key_text = QTextEdit() + ssh_key_text.setPlaceholderText("Paste SSH private key here...") + ssh_key_text.setFixedHeight(110) + + ssh_auth = existing.get("auth", {}) if existing.get("type") == "ssh" else {} + ssh_username.setText(ssh_auth.get("username", "")) + ssh_password.setText(ssh_auth.get("password", "")) + ssh_key_text.setPlainText(ssh_auth.get("key", "")) + # Default: use key if no password provided + ssh_use_key.setChecked(bool(ssh_auth.get("useKey", True if not ssh_password.text().strip() else False))) + + ssh_form.addRow(ssh_use_key) + ssh_form.addRow(ssh_user_lbl, ssh_username) + ssh_form.addRow(ssh_pass_lbl, ssh_password) + ssh_form.addRow(ssh_key_lbl, ssh_key_text) + + def _ssh_use_key_update(): + use_key = ssh_use_key.isChecked() + # Username always visible + ssh_user_lbl.setVisible(True) + ssh_username.setVisible(True) + # Toggle password vs key + ssh_pass_lbl.setVisible(not use_key) + ssh_password.setVisible(not use_key) + ssh_key_lbl.setVisible(use_key) + ssh_key_text.setVisible(use_key) + + ssh_use_key.stateChanged.connect(_ssh_use_key_update) + _ssh_use_key_update() + + # Stacked display controlled by _on_type_changed + # We keep all wraps created and toggle visibility. + auth_lay.addWidget(auth_title) + auth_lay.addWidget(smb_wrap) + auth_lay.addWidget(ftp_wrap) + auth_lay.addWidget(ssh_wrap) + + widgets["smb"] = { + "wrap": smb_wrap, + "guest": smb_guest, + "user_lbl": smb_user_lbl, + "username": smb_username, + "pass_lbl": smb_pass_lbl, + "password": smb_password, + } + widgets["ftp"] = { + "wrap": ftp_wrap, + "guest": ftp_guest, + "user_lbl": ftp_user_lbl, + "username": ftp_username, + "pass_lbl": ftp_pass_lbl, + "password": ftp_password, + } + widgets["ssh"] = { + "wrap": ssh_wrap, + "useKey": ssh_use_key, + "user_lbl": ssh_user_lbl, + "username": ssh_username, + "pass_lbl": ssh_pass_lbl, + "password": ssh_password, + "key_lbl": ssh_key_lbl, + "key": ssh_key_text, + } + + return type_combo, location_edit, port_spin, endpoint_row, auth_widget, widgets + + def _on_type_changed( + self, + type_combo: QComboBox, + location_edit: QLineEdit, + port_spin: QSpinBox, + auth_widget: QWidget, + widgets: Dict[str, Any], + initial: bool = False, + ): + typ = type_combo.currentText() + + # Placeholders + placeholder = { + "local": "Local path (e.g. /data or C:\\Data)", + "smb": "SMB path (e.g. \\\\SERVER\\Share\\Folder)", + "ftp": "FTP host/path (e.g. ftp.example.com:/folder)", + "ssh": "SSH host/path (e.g. example.com:/folder)", + }.get(typ, "") + location_edit.setPlaceholderText(placeholder) + + # Port visibility + defaults + if typ in ("ftp", "ssh"): + port_spin.setVisible(True) + + existing_type = type_combo.property("existing_type") or "local" + existing_auth = type_combo.property("existing_auth") or {} + + # If initial load and the saved endpoint type matches, prefer saved port + if initial and existing_type == typ: + if typ == "ftp": + port_spin.setValue(int(existing_auth.get("port", 21) or 21)) + else: # ssh + port_spin.setValue(int(existing_auth.get("port", 22) or 22)) + else: + # When switching types, fix common wrong/default values + cur = int(port_spin.value()) + if typ == "ftp" and cur in (0, 22): + port_spin.setValue(21) + elif typ == "ssh" and cur in (0, 21): + port_spin.setValue(22) + else: + port_spin.setVisible(False) + + # Auth visibility: collapse container when hidden, resize to content when shown + if typ == "local": + auth_widget.setVisible(False) + auth_widget.setMaximumHeight(0) + else: + auth_widget.setVisible(True) + auth_widget.setMaximumHeight(16777215) + + # Toggle which auth panel is visible + for key in ("smb", "ftp", "ssh"): + if key in widgets and "wrap" in widgets[key]: + widgets[key]["wrap"].setVisible(False) + + if typ in ("smb", "ftp", "ssh"): + widgets[typ]["wrap"].setVisible(True) + + # Defaults: guest ON for non-local SMB/FTP when not configured + if typ in ("smb", "ftp"): + w = widgets[typ] + if w["guest"].isChecked() is False: + # Only auto-enable guest if user/pass are empty + if not w["username"].text().strip() and not w["password"].text().strip(): + w["guest"].setChecked(True) + + # Force re-layout so the auth container shrinks/grows immediately + auth_widget.adjustSize() + if auth_widget.parentWidget() is not None: + auth_widget.parentWidget().adjustSize() + self.adjustSize() + + # ------------------------------------------------------------------ + # Validation + # ------------------------------------------------------------------ + def _on_ok(self): if not self.name.text().strip(): MsgBox.show(self, "Job", "Name is required.", icon="warning") return - if not self.source.text().strip(): - MsgBox.show(self, "Job", "Source is required.", icon="warning") + + src_type = self._source_type_combo.currentText() + tgt_type = self._target_type_combo.currentText() + + src_loc = self._source_location_edit.text().strip() + tgt_loc = self._target_location_edit.text().strip() + + if not src_loc: + MsgBox.show(self, "Job", "Source location is required.", icon="warning") return - if not self.target.text().strip(): - MsgBox.show(self, "Job", "Target is required.", icon="warning") + if not tgt_loc: + MsgBox.show(self, "Job", "Target location is required.", icon="warning") return + + # Source auth validation + if src_type in ("smb", "ftp"): + w = self._source_auth_widgets[src_type] + if not w["guest"].isChecked(): + if not w["username"].text().strip() or not w["password"].text().strip(): + MsgBox.show(self, "Job", "Source username and password are required.", icon="warning") + return + elif src_type == "ssh": + w = self._source_auth_widgets["ssh"] + if not w["username"].text().strip(): + MsgBox.show(self, "Job", "Source SSH username is required.", icon="warning") + return + use_key = w["useKey"].isChecked() + if use_key: + if not w["key"].toPlainText().strip(): + MsgBox.show(self, "Job", "Source SSH key is required when 'Use SSH Key' is enabled.", icon="warning") + return + else: + if not w["password"].text().strip(): + MsgBox.show(self, "Job", "Source SSH password is required when not using a key.", icon="warning") + return + + # Target auth validation + if tgt_type in ("smb", "ftp"): + w = self._target_auth_widgets[tgt_type] + if not w["guest"].isChecked(): + if not w["username"].text().strip() or not w["password"].text().strip(): + MsgBox.show(self, "Job", "Target username and password are required.", icon="warning") + return + elif tgt_type == "ssh": + w = self._target_auth_widgets["ssh"] + if not w["username"].text().strip(): + MsgBox.show(self, "Job", "Target SSH username is required.", icon="warning") + return + use_key = w["useKey"].isChecked() + if use_key: + if not w["key"].toPlainText().strip(): + MsgBox.show(self, "Job", "Target SSH key is required when 'Use SSH Key' is enabled.", icon="warning") + return + else: + if not w["password"].text().strip(): + MsgBox.show(self, "Job", "Target SSH password is required when not using a key.", icon="warning") + return + self.accept() + # ------------------------------------------------------------------ + # Value extraction + # ------------------------------------------------------------------ + def value(self) -> Dict[str, Any]: + def _extract( + type_combo: QComboBox, + location_edit: QLineEdit, + port_spin: QSpinBox, + widgets: Dict[str, Any], + ) -> Dict[str, Any]: + typ = type_combo.currentText() + location = location_edit.text().strip() + auth: Dict[str, Any] = {} + + if typ == "local": + auth = {} + elif typ == "smb": + w = widgets["smb"] + auth = { + "guest": bool(w["guest"].isChecked()), + "username": w["username"].text().strip(), + "password": w["password"].text(), + } + elif typ == "ftp": + w = widgets["ftp"] + auth = { + "guest": bool(w["guest"].isChecked()), + "username": w["username"].text().strip(), + "password": w["password"].text(), + "port": int(port_spin.value()), + } + elif typ == "ssh": + w = widgets["ssh"] + auth = { + "useKey": bool(w["useKey"].isChecked()), + "username": w["username"].text().strip(), + "password": w["password"].text(), + "port": int(port_spin.value()), + "key": w["key"].toPlainText(), + } + + return { + "type": typ, + "location": location, + "auth": auth, + } + + source_ep = _extract( + self._source_type_combo, + self._source_location_edit, + self._source_port_spin, + self._source_auth_widgets, + ) + target_ep = _extract( + self._target_type_combo, + self._target_location_edit, + self._target_port_spin, + self._target_auth_widgets, + ) + val: Dict[str, Any] = { "name": self.name.text().strip(), - "source": self.source.text().strip(), - "target": self.target.text().strip(), + "sourceEndpoint": source_ep, + "targetEndpoint": target_ep, + # Backward compatibility + "source": source_ep["location"], + "target": target_ep["location"], "allowDeletion": bool(self.allow_deletion.isChecked()), "preserveMetadata": bool(self.preserve_metadata.isChecked()), "mode": self.mode.currentText(), "direction": self.direction.currentText(), } + # Preserve schedule fields if they existed previously if self._original_job and isinstance(self._original_job, dict): sched = self._original_job.get("schedule") if sched is not None: val["schedule"] = sched + return val @@ -136,6 +708,8 @@ def __init__(self, parent=None, job: Optional[Dict[str, Any]] = None): schedule = job.get("schedule", {}) layout = QVBoxLayout(self) + layout.setAlignment(Qt.AlignTop) + layout.setSizeConstraint(QLayout.SetMinimumSize) form = QFormLayout() layout.addLayout(form) @@ -367,8 +941,22 @@ def _it(v: Any) -> QTableWidgetItem: return item self._table.setItem(r, 0, _it(job.get("name", ""))) - self._table.setItem(r, 1, _it(job.get("source", ""))) - self._table.setItem(r, 2, _it(job.get("target", ""))) + # Source column + src_str = "" + src_ep = job.get("sourceEndpoint") + if isinstance(src_ep, dict): + src_str = f"{src_ep.get('type', 'local')}:{src_ep.get('location', '')}" + else: + src_str = job.get("source", "") + self._table.setItem(r, 1, _it(src_str)) + # Target column + tgt_str = "" + tgt_ep = job.get("targetEndpoint") + if isinstance(tgt_ep, dict): + tgt_str = f"{tgt_ep.get('type', 'local')}:{tgt_ep.get('location', '')}" + else: + tgt_str = job.get("target", "") + self._table.setItem(r, 2, _it(tgt_str)) self._table.setItem(r, 3, _it(job.get("mode", "mirror"))) self._table.setItem(r, 4, _it(job.get("direction", "unidirectional"))) self._table.setItem(r, 5, _it("Yes" if job.get("allowDeletion") else "No")) @@ -492,8 +1080,21 @@ def run(self) -> bool: def _run_job(self, job: Dict[str, Any]) -> bool: name = job.get("name") or "Unnamed" - src = job.get("source") or "" - dst = job.get("target") or "" + # Determine src/dst and types + src_ep = job.get("sourceEndpoint") + if isinstance(src_ep, dict): + src = src_ep.get("location", "") + src_type = src_ep.get("type", "local") + else: + src = job.get("source") or "" + src_type = "local" + dst_ep = job.get("targetEndpoint") + if isinstance(dst_ep, dict): + dst = dst_ep.get("location", "") + dst_type = dst_ep.get("type", "local") + else: + dst = job.get("target") or "" + dst_type = "local" allow_deletion = bool(job.get("allowDeletion", False)) preserve_metadata = bool(job.get("preserveMetadata", True)) mode = job.get("mode", "mirror") @@ -507,7 +1108,13 @@ def _run_job(self, job: Dict[str, Any]) -> bool: sched_str = "" if schedule.get("enabled", False): sched_str = f", schedule=Every {schedule.get('everyMinutes', 60)} min" - self._log(f"[Replicator] Running job '{name}': {src} -> {dst} (mode={mode}, direction={direction}, delete={allow_deletion}, meta={preserve_metadata}{sched_str})") + logline = f"[Replicator] Running job '{name}': {src} -> {dst} (mode={mode}, direction={direction}, delete={allow_deletion}, meta={preserve_metadata}{sched_str}" + if isinstance(src_ep, dict): + logline += f", srcType={src_type}" + if isinstance(dst_ep, dict): + logline += f", dstType={dst_type}" + logline += ")" + self._log(logline) ok = self._fs.copy( src, From 8c82e75b83b31bb99291cc6b7a8a25cc85f89132 Mon Sep 17 00:00:00 2001 From: Louis Ouellet Date: Mon, 12 Jan 2026 14:22:24 -0500 Subject: [PATCH 04/51] General: Implementing a SQLite database to keep track of changes. --- .gitignore | 2 + src/core | 2 +- src/icons/logo.png | Bin 0 -> 315818 bytes src/main.py | 4 + src/replicator/replicator.py | 528 +++++++++++++++++++++++++++++++++-- 5 files changed, 508 insertions(+), 28 deletions(-) create mode 100644 src/icons/logo.png diff --git a/.gitignore b/.gitignore index 54779cc..1b4c0e1 100644 --- a/.gitignore +++ b/.gitignore @@ -22,9 +22,11 @@ runtime # Filetypes *.cfg *.log +*.db # Unique Directories /tmp/ +/data/ # Byte-compiled / optimized / DLL files __pycache__/ diff --git a/src/core b/src/core index 32f49cf..e656c79 160000 --- a/src/core +++ b/src/core @@ -1 +1 @@ -Subproject commit 32f49cf1d49c7be0c55b8ab4367785e18a03ee98 +Subproject commit e656c799f5a1166da593b0768a3c91f4955e369e diff --git a/src/icons/logo.png b/src/icons/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..a8bf604a23d2e8ee822a76e7e8825999c726855c GIT binary patch literal 315818 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�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

kK4B{uM_vytH_dOG^J(qw;jH7Z|Dmk5x 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

V;|+2?)H$8?~`H=w5r9C5E&w3HmxgtjLrz| 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�l1FYNeC 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 List[sqlite3.Row]: + conn = self.connect() + cur = conn.execute(sql, params) + return cur.fetchall() + + def query_one(self, sql: str, params: tuple = ()) -> Optional[sqlite3.Row]: + conn = self.connect() + cur = conn.execute(sql, params) + return cur.fetchone() + + def ensure_created_and_migrated(self): + # Ensure parent dir exists + db_dir = os.path.dirname(self._db_path) + os.makedirs(db_dir, exist_ok=True) + conn = self.connect() + # Schema migrations table + conn.execute(""" + CREATE TABLE IF NOT EXISTS schema_migrations ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + created DATETIME DEFAULT CURRENT_TIMESTAMP, + modified DATETIME DEFAULT CURRENT_TIMESTAMP, + name TEXT NOT NULL UNIQUE + ); + """) + # Try to create modified trigger for schema_migrations (best effort) + try: + conn.execute(""" + CREATE TRIGGER trg_schema_migrations_modified + BEFORE UPDATE ON schema_migrations + FOR EACH ROW + BEGIN + UPDATE schema_migrations SET modified = CURRENT_TIMESTAMP WHERE id = OLD.id; + END; + """) + except Exception: + pass + + # List of migrations (name, list-of-sql) + migrations = [ + ("0001_init", [ + # jobs + """ + CREATE TABLE IF NOT EXISTS jobs ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + created DATETIME DEFAULT CURRENT_TIMESTAMP, + modified DATETIME DEFAULT CURRENT_TIMESTAMP, + name TEXT NOT NULL, + enabled INTEGER NOT NULL DEFAULT 1, + mode TEXT NOT NULL DEFAULT 'mirror', + direction TEXT NOT NULL DEFAULT 'unidirectional', + allowDeletion INTEGER NOT NULL DEFAULT 0, + preserveMetadata INTEGER NOT NULL DEFAULT 1, + pairId TEXT NULL, + conflictPolicy TEXT NOT NULL DEFAULT 'newest', + lastRun TEXT NULL, + lastResult TEXT NULL, + lastError TEXT NULL + ); + """, + "CREATE INDEX IF NOT EXISTS idx_jobs_enabled ON jobs(enabled);", + # endpoints + """ + CREATE TABLE IF NOT EXISTS endpoints ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + created DATETIME DEFAULT CURRENT_TIMESTAMP, + modified DATETIME DEFAULT CURRENT_TIMESTAMP, + jobId INTEGER NOT NULL, + role TEXT NOT NULL, + type TEXT NOT NULL, + location TEXT NOT NULL, + port INTEGER NULL, + guest INTEGER NOT NULL DEFAULT 1, + username TEXT NULL, + password TEXT NULL, + useKey INTEGER NOT NULL DEFAULT 0, + sshKey TEXT NULL, + options TEXT NULL, + FOREIGN KEY(jobId) REFERENCES jobs(id) ON DELETE CASCADE + ); + """, + "CREATE UNIQUE INDEX IF NOT EXISTS uq_endpoints_job_role ON endpoints(jobId, role);", + # schedule + """ + CREATE TABLE IF NOT EXISTS schedule ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + created DATETIME DEFAULT CURRENT_TIMESTAMP, + modified DATETIME DEFAULT CURRENT_TIMESTAMP, + jobId INTEGER NOT NULL UNIQUE, + enabled INTEGER NOT NULL DEFAULT 0, + everyMinutes INTEGER NOT NULL DEFAULT 60, + nextRunAt TEXT NULL, + lastScheduledRunAt TEXT NULL, + FOREIGN KEY(jobId) REFERENCES jobs(id) ON DELETE CASCADE + ); + """, + # runs + """ + CREATE TABLE IF NOT EXISTS runs ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + created DATETIME DEFAULT CURRENT_TIMESTAMP, + modified DATETIME DEFAULT CURRENT_TIMESTAMP, + jobId INTEGER NOT NULL, + startedAt TEXT NOT NULL, + endedAt TEXT NULL, + result TEXT NOT NULL, + message TEXT NULL, + stats TEXT NULL, + FOREIGN KEY(jobId) REFERENCES jobs(id) ON DELETE CASCADE + ); + """, + "CREATE INDEX IF NOT EXISTS idx_runs_job_started ON runs(jobId, startedAt);", + # file_state + """ + CREATE TABLE IF NOT EXISTS file_state ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + created DATETIME DEFAULT CURRENT_TIMESTAMP, + modified DATETIME DEFAULT CURRENT_TIMESTAMP, + jobId INTEGER NOT NULL, + side TEXT NOT NULL, + relPath TEXT NOT NULL, + size INTEGER NOT NULL DEFAULT 0, + mtime INTEGER NOT NULL DEFAULT 0, + hash TEXT NULL, + isDir INTEGER NOT NULL DEFAULT 0, + deleted INTEGER NOT NULL DEFAULT 0, + deletedAt TEXT NULL, + meta TEXT NULL, + FOREIGN KEY(jobId) REFERENCES jobs(id) ON DELETE CASCADE + ); + """, + "CREATE UNIQUE INDEX IF NOT EXISTS uq_file_state ON file_state(jobId, side, relPath);", + # conflicts + """ + CREATE TABLE IF NOT EXISTS conflicts ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + created DATETIME DEFAULT CURRENT_TIMESTAMP, + modified DATETIME DEFAULT CURRENT_TIMESTAMP, + jobId INTEGER NOT NULL, + runId INTEGER NULL, + relPath TEXT NOT NULL, + a_size INTEGER NULL, + a_mtime INTEGER NULL, + a_hash TEXT NULL, + b_size INTEGER NULL, + b_mtime INTEGER NULL, + b_hash TEXT NULL, + status TEXT NOT NULL DEFAULT 'open', + resolution TEXT NULL, + note TEXT NULL, + FOREIGN KEY(jobId) REFERENCES jobs(id) ON DELETE CASCADE, + FOREIGN KEY(runId) REFERENCES runs(id) ON DELETE SET NULL + ); + """, + "CREATE INDEX IF NOT EXISTS idx_conflicts_job_status ON conflicts(jobId, status);", + ]), + ] + # Apply migrations in order + for name, stmts in migrations: + row = conn.execute("SELECT 1 FROM schema_migrations WHERE name = ?", (name,)).fetchone() + if not row: + try: + with conn: + for stmt in stmts: + conn.execute(stmt) + conn.execute("INSERT INTO schema_migrations (name) VALUES (?)", (name,)) + except Exception as e: + raise RuntimeError(f"Failed to apply migration {name}: {e}") + # Create modified triggers for all tables (best effort) + for tbl in ["jobs", "endpoints", "schedule", "runs", "file_state", "conflicts"]: + try: + self._ensure_modified_trigger(tbl) + except Exception: + pass + + def _ensure_modified_trigger(self, table_name: str): + # Try to create a BEFORE UPDATE trigger to set modified = CURRENT_TIMESTAMP + conn = self.connect() + trig_name = f"trg_{table_name}_modified" + # Try to drop if exists (to avoid duplicate triggers on repeated runs) + try: + conn.execute(f"DROP TRIGGER IF EXISTS {trig_name};") + except Exception: + pass + conn.execute(f""" + CREATE TRIGGER {trig_name} + BEFORE UPDATE ON {table_name} + FOR EACH ROW + BEGIN + UPDATE {table_name} SET modified = CURRENT_TIMESTAMP WHERE id = OLD.id; + END; + """) + + class JobDialog(QDialog): def __init__(self, parent=None, job: Optional[Dict[str, Any]] = None): @@ -750,9 +971,8 @@ def value(self) -> Dict[str, Any]: class Replicator(QMainWindow): """ Replicator UI + CLI entrypoint. - Jobs are stored in configuration under: replicator.jobs (list of dicts). + Jobs are stored in SQLite database (see ReplicatorDB). """ - def __init__( self, helper: Optional[Helper] = None, @@ -778,13 +998,28 @@ def __init__( self._fs = FileSystem(helper=self._helper, logger=self._logger) - # Ensure defaults exist + # Only keep configuration jobs for legacy import if self._configuration.get("replicator.jobs") is None: self._configuration.add("replicator.jobs", [], "json", label="Jobs") self._configuration.save() + # --- Database path setup --- + # Use Helper.get_cwd() if present, else os.getcwd() + if hasattr(self._helper, "get_cwd") and callable(getattr(self._helper, "get_cwd", None)): + base_dir = self._helper.get_cwd() + else: + base_dir = os.getcwd() + data_dir = os.path.join(base_dir, "data") + db_path = os.path.join(data_dir, "replicator.db") + + self._db = ReplicatorDB(db_path) + self._db.ensure_created_and_migrated() + self._log(f"[Replicator] Database: {db_path}", level="debug") + self._log(f"[Replicator] Database migrations applied.", level="info") + self._jobs: List[Dict[str, Any]] = [] self._table: Optional[QTableWidget] = None + self._legacy_import_done = False # ------------------------------------------------------------------ # CLI integration @@ -919,12 +1154,14 @@ def _selected_index(self) -> int: return rows[0].row() def _reload_jobs(self): - self._jobs = self._configuration.get("replicator.jobs", []) or [] + self._maybe_import_legacy_jobs() + self._jobs = self._db_fetch_jobs() self._refresh_table() def _save_jobs(self): - self._configuration.set("replicator.jobs", self._jobs) - self._configuration.save() + # No longer persists to config; upsert all jobs to DB (used by legacy code) + for job in self._jobs: + self._db_upsert_job(job) def _refresh_table(self): if not self._table: @@ -981,9 +1218,11 @@ def _it(v: Any) -> QTableWidgetItem: def _add_job(self): dlg = JobDialog(self) if dlg.exec_() == QDialog.Accepted: - self._jobs.append(dlg.value()) - self._save_jobs() - self._refresh_table() + new_job = dlg.value() + job_id = self._db_upsert_job(new_job) + new_job["id"] = job_id + self._jobs.append(new_job) + self._reload_jobs() def _edit_job(self): idx = self._selected_index() @@ -992,9 +1231,11 @@ def _edit_job(self): return dlg = JobDialog(self, self._jobs[idx]) if dlg.exec_() == QDialog.Accepted: - self._jobs[idx] = dlg.value() - self._save_jobs() - self._refresh_table() + updated_job = dlg.value() + # preserve id + updated_job["id"] = self._jobs[idx].get("id") + self._db_upsert_job(updated_job) + self._reload_jobs() def _duplicate_job(self): idx = self._selected_index() @@ -1011,9 +1252,11 @@ def _duplicate_job(self): new_job["name"] = f"{name} (copy)" else: new_job["name"] = "Copy" - self._jobs.append(new_job) - self._save_jobs() - self._refresh_table() + if "id" in new_job: + del new_job["id"] + job_id = self._db_upsert_job(new_job) + new_job["id"] = job_id + self._reload_jobs() # Select the new row if self._table: new_row = self._table.rowCount() - 1 @@ -1033,9 +1276,10 @@ def _delete_job(self): default="Cancel", ) if choice == "Delete": - self._jobs.pop(idx) - self._save_jobs() - self._refresh_table() + job_id = self._jobs[idx].get("id") + if job_id: + self._db_delete_job(job_id) + self._reload_jobs() def _edit_schedule(self): idx = self._selected_index() @@ -1044,9 +1288,10 @@ def _edit_schedule(self): return dlg = ScheduleDialog(self, job=self._jobs[idx]) if dlg.exec_() == QDialog.Accepted: - self._jobs[idx]["schedule"] = dlg.value() - self._save_jobs() - self._refresh_table() + job = self._jobs[idx] + job["schedule"] = dlg.value() + self._db_upsert_job(job) + self._reload_jobs() def _run_with_ui_feedback(self): ok = self.run() @@ -1063,10 +1308,11 @@ def _run_with_ui_feedback(self): def run(self) -> bool: """ - Run all configured jobs. + Run all configured jobs (from DB). Returns True if all jobs succeeded. """ - jobs = self._configuration.get("replicator.jobs", []) or [] + self._reload_jobs() + jobs = self._jobs if not jobs: self._log("[Replicator] No jobs configured.", level="warning") return False @@ -1080,6 +1326,7 @@ def run(self) -> bool: def _run_job(self, job: Dict[str, Any]) -> bool: name = job.get("name") or "Unnamed" + job_id = job.get("id") # Determine src/dst and types src_ep = job.get("sourceEndpoint") if isinstance(src_ep, dict): @@ -1116,6 +1363,18 @@ def _run_job(self, job: Dict[str, Any]) -> bool: logline += ")" self._log(logline) + # Insert a run record at start + started_at = datetime.now(timezone.utc).isoformat() + run_id = None + try: + cur = self._db.execute( + "INSERT INTO runs (jobId, startedAt, result) VALUES (?, ?, ?)", + (job_id, started_at, "running"), + ) + run_id = cur.lastrowid + except Exception as e: + self._log(f"[Replicator] Failed to insert run record: {e}", level="warning") + ok = self._fs.copy( src, dst, @@ -1123,12 +1382,22 @@ def _run_job(self, job: Dict[str, Any]) -> bool: allow_deletion=allow_deletion, ) - # Persist lastRun and lastResult, refresh UI, save - job["lastRun"] = datetime.now(timezone.utc).isoformat() + # Persist lastRun and lastResult, refresh UI, save to DB + now_str = datetime.now(timezone.utc).isoformat() + job["lastRun"] = now_str job["lastResult"] = "ok" if ok else "fail" - self._save_jobs() + job["lastError"] = None if ok else "Failed" + self._db_upsert_job(job) + if run_id: + try: + self._db.execute( + "UPDATE runs SET endedAt = ?, result = ?, message = ? WHERE id = ?", + (now_str, "ok" if ok else "fail", None if ok else "Failed", run_id), + ) + except Exception as e: + self._log(f"[Replicator] Failed to update run record: {e}", level="warning") if self._table: - self._refresh_table() + self._reload_jobs() self._log(f"[Replicator] Job '{name}' result: {'OK' if ok else 'FAIL'} (lastResult={job['lastResult']})", level="info" if ok else "warning") return ok @@ -1138,3 +1407,208 @@ def _log(self, msg: str, level: str = "info", channel: str = "replicator") -> No self._logger.append(msg, level=level, channel=channel) # type: ignore[call-arg] else: print(msg) + + +# ------------------------------------------------------------------ +# DB-backed job persistence methods +# ------------------------------------------------------------------ + def _db_fetch_jobs(self) -> List[Dict[str, Any]]: + # Query jobs ordered by id + rows = self._db.query_all("SELECT * FROM jobs ORDER BY id ASC") + jobs: List[Dict[str, Any]] = [] + for row in rows: + job = dict(row) + job["id"] = row["id"] + # Endpoints + eps = self._db.query_all("SELECT * FROM endpoints WHERE jobId = ?", (row["id"],)) + for ep in eps: + ep_d = dict(ep) + auth = {} + # Compose auth dict + if ep_d["type"] == "local": + auth = {} + elif ep_d["type"] in ("smb", "ftp"): + auth = { + "guest": bool(ep_d.get("guest", 1)), + "username": ep_d.get("username") or "", + "password": ep_d.get("password") or "", + } + if ep_d["type"] == "ftp": + auth["port"] = ep_d.get("port") or 21 + elif ep_d["type"] == "ssh": + auth = { + "useKey": bool(ep_d.get("useKey", 0)), + "username": ep_d.get("username") or "", + "password": ep_d.get("password") or "", + "port": ep_d.get("port") or 22, + "key": ep_d.get("sshKey") or "", + } + # Add any JSON options + if ep_d.get("options"): + try: + auth.update(json.loads(ep_d["options"])) + except Exception: + pass + ep_obj = { + "type": ep_d["type"], + "location": ep_d["location"], + "auth": auth, + } + if ep_d["role"] == "source": + job["sourceEndpoint"] = ep_obj + job["source"] = ep_d["location"] + elif ep_d["role"] == "target": + job["targetEndpoint"] = ep_obj + job["target"] = ep_d["location"] + # Schedule + sched_row = self._db.query_one("SELECT * FROM schedule WHERE jobId = ?", (row["id"],)) + if sched_row: + job["schedule"] = { + "enabled": bool(sched_row["enabled"]), + "everyMinutes": sched_row["everyMinutes"], + } + else: + job["schedule"] = {"enabled": False, "everyMinutes": 60} + # lastRun/lastResult/lastError + job["lastRun"] = row["lastRun"] + job["lastResult"] = row["lastResult"] + job["lastError"] = row["lastError"] + jobs.append(job) + return jobs + + def _db_upsert_job(self, job: Dict[str, Any]) -> int: + # Insert or update jobs row, endpoints, schedule (transaction) + conn = self._db.connect() + with conn: + # Upsert job + fields = [ + "name", "enabled", "mode", "direction", "allowDeletion", "preserveMetadata", + "pairId", "conflictPolicy", "lastRun", "lastResult", "lastError" + ] + values = [ + job.get("name"), + 1 if job.get("enabled", True) else 0, + job.get("mode", "mirror"), + job.get("direction", "unidirectional"), + 1 if job.get("allowDeletion", False) else 0, + 1 if job.get("preserveMetadata", True) else 0, + job.get("pairId"), + job.get("conflictPolicy", "newest"), + job.get("lastRun"), + job.get("lastResult"), + job.get("lastError"), + ] + if job.get("id"): + # UPDATE + set_clause = ", ".join(f"{f}=?" for f in fields) + conn.execute( + f"UPDATE jobs SET {set_clause} WHERE id = ?", + tuple(values) + (job["id"],) + ) + job_id = job["id"] + else: + # INSERT + placeholders = ", ".join("?" for _ in fields) + cur = conn.execute( + f"INSERT INTO jobs ({', '.join(fields)}) VALUES ({placeholders})", + tuple(values) + ) + job_id = cur.lastrowid + job["id"] = job_id + + # Endpoints (upsert by (jobId, role)) + for role in ("source", "target"): + ep = job.get("sourceEndpoint") if role == "source" else job.get("targetEndpoint") + if not isinstance(ep, dict): + continue + ep_type = ep.get("type", "local") + location = ep.get("location", "") + auth = ep.get("auth", {}) or {} + # Compose columns + port = None + guest = 1 + username = None + password = None + useKey = 0 + sshKey = None + options = {} + if ep_type == "local": + pass + elif ep_type in ("smb", "ftp"): + guest = 1 if auth.get("guest", True) else 0 + username = auth.get("username") + password = auth.get("password") + if ep_type == "ftp": + port = int(auth.get("port", 21)) + elif ep_type == "ssh": + useKey = 1 if auth.get("useKey", False) else 0 + username = auth.get("username") + password = auth.get("password") + port = int(auth.get("port", 22)) + sshKey = auth.get("key") + # Store any extra keys in options + known_keys = {"guest", "username", "password", "port", "useKey", "key"} + for k, v in auth.items(): + if k not in known_keys: + options[k] = v + # Upsert: try update first, else insert + ep_row = conn.execute( + "SELECT id FROM endpoints WHERE jobId = ? AND role = ?", + (job_id, role) + ).fetchone() + ep_fields = [ + "jobId", "role", "type", "location", "port", "guest", "username", "password", "useKey", "sshKey", "options" + ] + ep_values = [ + job_id, role, ep_type, location, port, guest, username, password, useKey, sshKey, + json.dumps(options) if options else None + ] + if ep_row: + set_clause = ", ".join(f"{f}=?" for f in ep_fields[2:]) # skip jobId, role + conn.execute( + f"UPDATE endpoints SET {set_clause} WHERE jobId=? AND role=?", + tuple(ep_values[2:]) + (job_id, role) + ) + else: + placeholders = ", ".join("?" for _ in ep_fields) + conn.execute( + f"INSERT INTO endpoints ({', '.join(ep_fields)}) VALUES ({placeholders})", + tuple(ep_values) + ) + # Schedule + sched = job.get("schedule") + if sched: + sched_row = conn.execute( + "SELECT id FROM schedule WHERE jobId = ?", (job_id,) + ).fetchone() + enabled = 1 if sched.get("enabled", False) else 0 + every = int(sched.get("everyMinutes", 60)) + if sched_row: + conn.execute( + "UPDATE schedule SET enabled=?, everyMinutes=? WHERE jobId=?", + (enabled, every, job_id) + ) + else: + conn.execute( + "INSERT INTO schedule (jobId, enabled, everyMinutes) VALUES (?, ?, ?)", + (job_id, enabled, every) + ) + return job.get("id") + + def _db_delete_job(self, job_id: int) -> None: + self._db.execute("DELETE FROM jobs WHERE id = ?", (job_id,)) + + def _maybe_import_legacy_jobs(self): + if getattr(self, "_legacy_import_done", False): + return + self._legacy_import_done = True + # Only import if jobs table is empty and legacy config has jobs + cnt = self._db.query_one("SELECT COUNT(*) AS cnt FROM jobs") + if cnt and cnt["cnt"] == 0: + jobs = self._configuration.get("replicator.jobs", []) or [] + if jobs: + imported = 0 + for job in jobs: + self._db_upsert_job(job) + imported += 1 + self._log(f"[Replicator] Imported {imported} jobs from legacy configuration.", level="info") From 9d0f3974b4f720b1b6703e7ab6200aece6141eee Mon Sep 17 00:00:00 2001 From: Louis Ouellet Date: Mon, 12 Jan 2026 15:07:12 -0500 Subject: [PATCH 05/51] BUGFIX: Addressing sync issues in bidirectional sync. --- src/replicator/replicator.py | 525 +++++++++++++++++++++++++++++++++-- 1 file changed, 496 insertions(+), 29 deletions(-) diff --git a/src/replicator/replicator.py b/src/replicator/replicator.py index d6ef8a7..6cbd341 100644 --- a/src/replicator/replicator.py +++ b/src/replicator/replicator.py @@ -5,10 +5,11 @@ from typing import Optional, Any, Dict, List -# --- New imports for DB --- import os import sqlite3 import json +import shutil +import hashlib # Add datetime import for lastRun/lastResult from datetime import datetime, timezone @@ -969,6 +970,423 @@ def value(self) -> Dict[str, Any]: class Replicator(QMainWindow): + + # ------------------------------------------------------------------ + # Bidirectional sync helpers (local-only for now) + # ------------------------------------------------------------------ + + def _endpoint_local_root(self, ep: Dict[str, Any]) -> str: + """Return the local root path for an endpoint or raise.""" + if not isinstance(ep, dict): + raise ValueError("Endpoint is missing") + typ = (ep.get("type") or "local").lower() + if typ != "local": + raise NotImplementedError(f"Bidirectional sync is currently implemented for local endpoints only (got '{typ}').") + loc = (ep.get("location") or "").strip() + if not loc: + raise ValueError("Endpoint location is required") + return loc + + def _scan_local_tree(self, root: str) -> Dict[str, Dict[str, Any]]: + """Return a map relPath -> {isDir,size,mtime} for a local filesystem root.""" + root = os.path.abspath(root) + out: Dict[str, Dict[str, Any]] = {} + if not os.path.exists(root): + return out + + # Walk directories; include dirs as entries so deletions can be propagated. + for dirpath, dirnames, filenames in os.walk(root): + # Normalize and skip hidden special entries if needed (keep simple for now) + rel_dir = os.path.relpath(dirpath, root) + if rel_dir == ".": + rel_dir = "" + + # Record directory itself (except root) + if rel_dir: + try: + st = os.stat(dirpath) + out[rel_dir] = { + "isDir": True, + "size": 0, + "mtime": int(st.st_mtime), + } + except Exception: + # If stat fails, still record directory + out[rel_dir] = {"isDir": True, "size": 0, "mtime": 0} + + for fn in filenames: + full = os.path.join(dirpath, fn) + rel = os.path.relpath(full, root) + try: + st = os.stat(full) + out[rel] = { + "isDir": False, + "size": int(st.st_size), + "mtime": int(st.st_mtime), + } + except Exception: + out[rel] = {"isDir": False, "size": 0, "mtime": 0} + + return out + + def _load_prev_file_state(self, job_id: int, side: str) -> Dict[str, Dict[str, Any]]: + rows = self._db.query_all( + "SELECT relPath, size, mtime, isDir, deleted FROM file_state WHERE jobId=? AND side=?", + (job_id, side), + ) + prev: Dict[str, Dict[str, Any]] = {} + for r in rows: + prev[str(r["relPath"])] = { + "size": int(r["size"] or 0), + "mtime": int(r["mtime"] or 0), + "isDir": bool(r["isDir"]), + "deleted": bool(r["deleted"]), + } + return prev + + def _persist_file_state(self, job_id: int, side: str, cur: Dict[str, Dict[str, Any]]) -> None: + """Upsert current snapshot into file_state and mark missing as deleted.""" + conn = self._db.connect() + now_iso = datetime.now(timezone.utc).isoformat() + with conn: + # Mark everything as deleted first; we will clear deleted for entries that exist now. + conn.execute( + "UPDATE file_state SET deleted=1, deletedAt=? WHERE jobId=? AND side=?", + (now_iso, job_id, side), + ) + for rel, meta in cur.items(): + is_dir = 1 if meta.get("isDir") else 0 + size = int(meta.get("size", 0) or 0) + mtime = int(meta.get("mtime", 0) or 0) + + row = conn.execute( + "SELECT id FROM file_state WHERE jobId=? AND side=? AND relPath=?", + (job_id, side, rel), + ).fetchone() + if row: + conn.execute( + "UPDATE file_state SET size=?, mtime=?, isDir=?, deleted=0, deletedAt=NULL WHERE jobId=? AND side=? AND relPath=?", + (size, mtime, is_dir, job_id, side, rel), + ) + else: + conn.execute( + "INSERT INTO file_state (jobId, side, relPath, size, mtime, isDir, deleted) VALUES (?,?,?,?,?,?,0)", + (job_id, side, rel, size, mtime, is_dir), + ) + + def _ensure_parent_dir(self, path: str) -> None: + parent = os.path.dirname(path) + if parent and not os.path.exists(parent): + os.makedirs(parent, exist_ok=True) + + def _copy_local_path(self, src_root: str, dst_root: str, rel: str, is_dir: bool, preserve_metadata: bool) -> None: + src_full = os.path.join(src_root, rel) + dst_full = os.path.join(dst_root, rel) + + if is_dir: + os.makedirs(dst_full, exist_ok=True) + return + + self._ensure_parent_dir(dst_full) + if preserve_metadata: + shutil.copy2(src_full, dst_full) + else: + shutil.copy(src_full, dst_full) + + def _delete_local_path(self, root: str, rel: str, is_dir: bool) -> None: + full = os.path.join(root, rel) + if not os.path.exists(full): + return + if is_dir: + # Only remove if empty; never rmtree blindly in sync engine. + try: + os.rmdir(full) + except OSError: + pass + else: + try: + os.remove(full) + except Exception: + pass + + def _record_conflict( + self, + job_id: int, + run_id: Optional[int], + rel: str, + a: Dict[str, Any], + b: Dict[str, Any], + note: str = "", + ) -> None: + try: + self._db.execute( + """ + INSERT INTO conflicts (jobId, runId, relPath, a_size, a_mtime, a_hash, b_size, b_mtime, b_hash, status, note) + VALUES (?,?,?,?,?,?,?,?,?,'open',?) + """, + ( + job_id, + run_id, + rel, + int(a.get("size", 0) or 0), + int(a.get("mtime", 0) or 0), + a.get("hash"), + int(b.get("size", 0) or 0), + int(b.get("mtime", 0) or 0), + b.get("hash"), + note or None, + ), + ) + except Exception as e: + self._log(f"[Replicator][DB] Failed to record conflict for '{rel}': {e}", level="warning") + + def _run_job_bidirectional(self, job: Dict[str, Any], run_id: Optional[int]) -> bool: + """Bidirectional sync using `file_state` as the persistent baseline. + + Currently implemented for local<->local only. + """ + job_id = int(job.get("id") or 0) + if job_id <= 0: + raise ValueError("Job must have an id to run bidirectional sync") + + src_ep = job.get("sourceEndpoint") + dst_ep = job.get("targetEndpoint") + a_root = self._endpoint_local_root(src_ep) + b_root = self._endpoint_local_root(dst_ep) + + allow_deletion = bool(job.get("allowDeletion", False)) + preserve_metadata = bool(job.get("preserveMetadata", True)) + conflict_policy = (job.get("conflictPolicy") or "newest").lower() + + # Load previous baseline before scanning/persisting. + prev_a = self._load_prev_file_state(job_id, "A") + prev_b = self._load_prev_file_state(job_id, "B") + + # Scan current trees + cur_a = self._scan_local_tree(a_root) + cur_b = self._scan_local_tree(b_root) + + # Persist current snapshot (baseline for next run) + self._persist_file_state(job_id, "A", cur_a) + self._persist_file_state(job_id, "B", cur_b) + + def _meta_changed(cur: Dict[str, Any], prev: Dict[str, Any]) -> bool: + return ( + bool(cur.get("isDir")) != bool(prev.get("isDir")) + or int(cur.get("size", 0) or 0) != int(prev.get("size", 0) or 0) + or int(cur.get("mtime", 0) or 0) != int(prev.get("mtime", 0) or 0) + ) + + def _changed_set(cur: Dict[str, Dict[str, Any]], prev: Dict[str, Dict[str, Any]]) -> set[str]: + out = set() + for rel, meta in cur.items(): + p = prev.get(rel) + if p is None or p.get("deleted", False): + out.add(rel) + else: + if _meta_changed(meta, p): + out.add(rel) + return out + + def _deleted_set(cur: Dict[str, Dict[str, Any]], prev: Dict[str, Dict[str, Any]]) -> set[str]: + # deleted since last baseline means it existed (not deleted) and now missing + out = set() + for rel, p in prev.items(): + if p.get("deleted", False): + continue + if rel not in cur: + out.add(rel) + return out + + changed_a = _changed_set(cur_a, prev_a) + changed_b = _changed_set(cur_b, prev_b) + deleted_a = _deleted_set(cur_a, prev_a) + deleted_b = _deleted_set(cur_b, prev_b) + + self._log( + f"[Replicator][BiDi] Snapshot A={len(cur_a)} entries, B={len(cur_b)} entries; changedA={len(changed_a)} changedB={len(changed_b)} deletedA={len(deleted_a)} deletedB={len(deleted_b)}", + level="debug", + ) + + # Build unified rel set + all_paths = set(cur_a.keys()) | set(cur_b.keys()) | set(prev_a.keys()) | set(prev_b.keys()) + + actions_copy_a_to_b: list[tuple[str, bool]] = [] + actions_copy_b_to_a: list[tuple[str, bool]] = [] + actions_del_a: list[tuple[str, bool]] = [] + actions_del_b: list[tuple[str, bool]] = [] + + # Helper for newest + def _winner_newest(a: Dict[str, Any], b: Dict[str, Any]) -> str: + am = int(a.get("mtime", 0) or 0) + bm = int(b.get("mtime", 0) or 0) + if am == bm: + # tie-breaker: larger size wins + return "A" if int(a.get("size", 0) or 0) >= int(b.get("size", 0) or 0) else "B" + return "A" if am > bm else "B" + + for rel in sorted(all_paths): + a_cur = cur_a.get(rel) + b_cur = cur_b.get(rel) + + a_exists = a_cur is not None + b_exists = b_cur is not None + + a_changed = rel in changed_a + b_changed = rel in changed_b + + a_deleted = rel in deleted_a + b_deleted = rel in deleted_b + + # If exists only on one side. + # When deletions are enabled and the missing side deleted it since the last baseline, + # deletion should win unless the existing side also changed (conflict). + if a_exists and not b_exists: + if b_deleted and allow_deletion: + if a_changed: + # conflict: B deleted while A changed + self._record_conflict(job_id, run_id, rel, a_cur, {"deleted": True}, note="B deleted, A changed") + winner = "A" if conflict_policy == "newest" else "A" + if winner == "A": + actions_copy_a_to_b.append((rel, bool(a_cur.get("isDir")))) + else: + actions_del_a.append((rel, bool(a_cur.get("isDir")))) + else: + # propagate deletion (keep B deleted, delete A) + actions_del_a.append((rel, bool(a_cur.get("isDir")))) + else: + # no deletion involved: treat as create on A, copy to B + actions_copy_a_to_b.append((rel, bool(a_cur.get("isDir")))) + continue + + if b_exists and not a_exists: + if a_deleted and allow_deletion: + if b_changed: + # conflict: A deleted while B changed + self._record_conflict(job_id, run_id, rel, {"deleted": True}, b_cur, note="A deleted, B changed") + winner = "B" if conflict_policy == "newest" else "B" + if winner == "B": + actions_copy_b_to_a.append((rel, bool(b_cur.get("isDir")))) + else: + actions_del_b.append((rel, bool(b_cur.get("isDir")))) + else: + # propagate deletion (keep A deleted, delete B) + actions_del_b.append((rel, bool(b_cur.get("isDir")))) + else: + actions_copy_b_to_a.append((rel, bool(b_cur.get("isDir")))) + continue + + # Missing on both sides: maybe deletion propagation; nothing to do. + if not a_exists and not b_exists: + continue + + # Exists on both: check if identical + if a_exists and b_exists: + same = ( + bool(a_cur.get("isDir")) == bool(b_cur.get("isDir")) + and int(a_cur.get("size", 0) or 0) == int(b_cur.get("size", 0) or 0) + and int(a_cur.get("mtime", 0) or 0) == int(b_cur.get("mtime", 0) or 0) + ) + + if same: + # Maybe propagate deletions if one side deleted previously (should not happen if same exists) + continue + + # Not same: detect changes since baseline + if a_changed and b_changed: + # true conflict + self._record_conflict(job_id, run_id, rel, a_cur, b_cur, note="Changed on both sides") + if conflict_policy == "newest": + winner = _winner_newest(a_cur, b_cur) + elif conflict_policy in ("keepa", "a"): + winner = "A" + elif conflict_policy in ("keepb", "b"): + winner = "B" + else: + winner = _winner_newest(a_cur, b_cur) + + if winner == "A": + actions_copy_a_to_b.append((rel, bool(a_cur.get("isDir")))) + else: + actions_copy_b_to_a.append((rel, bool(b_cur.get("isDir")))) + elif a_changed and not b_changed: + actions_copy_a_to_b.append((rel, bool(a_cur.get("isDir")))) + elif b_changed and not a_changed: + actions_copy_b_to_a.append((rel, bool(b_cur.get("isDir")))) + else: + # differs but we can't prove which changed vs baseline (e.g., first run with baseline empty) + # Choose newest as default. + winner = _winner_newest(a_cur, b_cur) + if winner == "A": + actions_copy_a_to_b.append((rel, bool(a_cur.get("isDir")))) + else: + actions_copy_b_to_a.append((rel, bool(b_cur.get("isDir")))) + + # Deletions propagation (safe rules) + if allow_deletion: + # If A deleted and B did not change since baseline, delete on B + for rel in sorted(deleted_a): + if rel in changed_b: + # conflict: deleted on A but changed on B + self._record_conflict(job_id, run_id, rel, {"deleted": True}, cur_b.get(rel, {}), note="A deleted, B changed") + continue + pb = prev_b.get(rel) + if pb and not pb.get("deleted", False) and rel in cur_b: + actions_del_b.append((rel, bool(cur_b[rel].get("isDir")))) + + # If B deleted and A did not change since baseline, delete on A + for rel in sorted(deleted_b): + if rel in changed_a: + self._record_conflict(job_id, run_id, rel, cur_a.get(rel, {}), {"deleted": True}, note="B deleted, A changed") + continue + pa = prev_a.get(rel) + if pa and not pa.get("deleted", False) and rel in cur_a: + actions_del_a.append((rel, bool(cur_a[rel].get("isDir")))) + + # Execute actions + try: + # Copy directories first to ensure parents exist + for rel, is_dir in actions_copy_a_to_b: + self._copy_local_path(a_root, b_root, rel, is_dir, preserve_metadata) + for rel, is_dir in actions_copy_b_to_a: + self._copy_local_path(b_root, a_root, rel, is_dir, preserve_metadata) + + # Then copy files (copy method handles both, but ordering helps for deep paths) + # (Already handled in _copy_local_path) + + # Deletions last + for rel, is_dir in actions_del_a: + self._delete_local_path(a_root, rel, is_dir) + for rel, is_dir in actions_del_b: + self._delete_local_path(b_root, rel, is_dir) + + except Exception as e: + self._log(f"[Replicator][BiDi] Execution failed: {e}", level="error") + return False + + stats = { + "copyAtoB": len(actions_copy_a_to_b), + "copyBtoA": len(actions_copy_b_to_a), + "delA": len(actions_del_a), + "delB": len(actions_del_b), + "changedA": len(changed_a), + "changedB": len(changed_b), + "deletedA": len(deleted_a), + "deletedB": len(deleted_b), + } + self._log(f"[Replicator][BiDi] Actions: {stats}", level="debug") + + # Update run stats if possible + if run_id: + try: + self._db.execute( + "UPDATE runs SET stats=? WHERE id=?", + (json.dumps(stats), run_id), + ) + except Exception: + pass + + return True """ Replicator UI + CLI entrypoint. Jobs are stored in SQLite database (see ReplicatorDB). @@ -998,10 +1416,7 @@ def __init__( self._fs = FileSystem(helper=self._helper, logger=self._logger) - # Only keep configuration jobs for legacy import - if self._configuration.get("replicator.jobs") is None: - self._configuration.add("replicator.jobs", [], "json", label="Jobs") - self._configuration.save() + # Legacy configuration jobs bootstrap removed. # --- Database path setup --- # Use Helper.get_cwd() if present, else os.getcwd() @@ -1019,7 +1434,9 @@ def __init__( self._jobs: List[Dict[str, Any]] = [] self._table: Optional[QTableWidget] = None - self._legacy_import_done = False + + # After DB is ready, log a snapshot of DB layout/counts (non-verbose) + self._db_debug_snapshot(verbose=False) # ------------------------------------------------------------------ # CLI integration @@ -1154,7 +1571,6 @@ def _selected_index(self) -> int: return rows[0].row() def _reload_jobs(self): - self._maybe_import_legacy_jobs() self._jobs = self._db_fetch_jobs() self._refresh_table() @@ -1312,6 +1728,8 @@ def run(self) -> bool: Returns True if all jobs succeeded. """ self._reload_jobs() + # After reloading jobs, log a verbose DB snapshot for debugging + self._db_debug_snapshot(verbose=True) jobs = self._jobs if not jobs: self._log("[Replicator] No jobs configured.", level="warning") @@ -1375,24 +1793,35 @@ def _run_job(self, job: Dict[str, Any]) -> bool: except Exception as e: self._log(f"[Replicator] Failed to insert run record: {e}", level="warning") - ok = self._fs.copy( - src, - dst, - preserve_metadata=preserve_metadata, - allow_deletion=allow_deletion, - ) + ok = False + try: + if str(direction).lower() == "bidirectional": + ok = self._run_job_bidirectional(job, run_id) + else: + ok = self._fs.copy( + src, + dst, + preserve_metadata=preserve_metadata, + allow_deletion=allow_deletion, + ) + except NotImplementedError as e: + self._log(f"[Replicator] Job '{name}' not supported: {e}", level="error") + ok = False + except Exception as e: + self._log(f"[Replicator] Job '{name}' failed: {e}", level="error") + ok = False # Persist lastRun and lastResult, refresh UI, save to DB now_str = datetime.now(timezone.utc).isoformat() job["lastRun"] = now_str job["lastResult"] = "ok" if ok else "fail" - job["lastError"] = None if ok else "Failed" + job["lastError"] = None if ok else (job.get("lastError") or "Failed") self._db_upsert_job(job) if run_id: try: self._db.execute( "UPDATE runs SET endedAt = ?, result = ?, message = ? WHERE id = ?", - (now_str, "ok" if ok else "fail", None if ok else "Failed", run_id), + (now_str, "ok" if ok else "fail", None if ok else (job.get("lastError") or "Failed"), run_id), ) except Exception as e: self._log(f"[Replicator] Failed to update run record: {e}", level="warning") @@ -1408,6 +1837,58 @@ def _log(self, msg: str, level: str = "info", channel: str = "replicator") -> No else: print(msg) + def _db_debug_snapshot(self, verbose: bool = False) -> None: + """ + Log a compact snapshot of the current DB layout + row counts. + If verbose=True, also logs the most recent rows for key tables. + """ + try: + conn = self._db.connect() + + # List tables + tables = [r["name"] for r in conn.execute( + "SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' ORDER BY name" + ).fetchall()] + + self._log(f"[Replicator][DB] Tables: {', '.join(tables) if tables else '(none)'}", level="debug") + + # Layout + counts + for t in tables: + try: + cols = conn.execute(f"PRAGMA table_info({t})").fetchall() + col_names = [c[1] for c in cols] # (cid, name, type, notnull, dflt_value, pk) + count = conn.execute(f"SELECT COUNT(*) FROM {t}").fetchone()[0] + self._log(f"[Replicator][DB] {t}: columns={len(col_names)} rows={count}", level="debug") + if verbose: + self._log(f"[Replicator][DB] {t}: {', '.join(col_names)}", level="debug") + except Exception as e: + self._log(f"[Replicator][DB] Failed introspecting {t}: {e}", level="warning") + + if not verbose: + return + + # Recent rows (lightweight, avoid dumping secrets) + def _safe_row(row: sqlite3.Row) -> Dict[str, Any]: + d = dict(row) + # redact sensitive fields if present + for k in ("password", "sshKey"): + if k in d and d[k]: + d[k] = "***" + return d + + for t, order_col in (("jobs", "id"), ("runs", "id"), ("schedule", "id"), ("conflicts", "id")): + if t in tables: + try: + rows = conn.execute(f"SELECT * FROM {t} ORDER BY {order_col} DESC LIMIT 3").fetchall() + self._log(f"[Replicator][DB] {t}: latest {len(rows)} row(s)", level="debug") + for r in rows: + self._log(f"[Replicator][DB] {t}: {_safe_row(r)}", level="debug") + except Exception as e: + self._log(f"[Replicator][DB] Failed reading latest rows from {t}: {e}", level="warning") + + except Exception as e: + self._log(f"[Replicator][DB] Snapshot failed: {e}", level="warning") + # ------------------------------------------------------------------ # DB-backed job persistence methods @@ -1598,17 +2079,3 @@ def _db_upsert_job(self, job: Dict[str, Any]) -> int: def _db_delete_job(self, job_id: int) -> None: self._db.execute("DELETE FROM jobs WHERE id = ?", (job_id,)) - def _maybe_import_legacy_jobs(self): - if getattr(self, "_legacy_import_done", False): - return - self._legacy_import_done = True - # Only import if jobs table is empty and legacy config has jobs - cnt = self._db.query_one("SELECT COUNT(*) AS cnt FROM jobs") - if cnt and cnt["cnt"] == 0: - jobs = self._configuration.get("replicator.jobs", []) or [] - if jobs: - imported = 0 - for job in jobs: - self._db_upsert_job(job) - imported += 1 - self._log(f"[Replicator] Imported {imported} jobs from legacy configuration.", level="info") From 196d0941eeed9090c6f3c37bd9e63c5ba32621ef Mon Sep 17 00:00:00 2001 From: Louis Ouellet Date: Mon, 12 Jan 2026 15:31:11 -0500 Subject: [PATCH 06/51] BUGFIX: Addressing sync issues in bidirectional sync. --- src/replicator/replicator.py | 58 +++++++++++++++++++++++++----------- 1 file changed, 41 insertions(+), 17 deletions(-) diff --git a/src/replicator/replicator.py b/src/replicator/replicator.py index 6cbd341..0b4897d 100644 --- a/src/replicator/replicator.py +++ b/src/replicator/replicator.py @@ -232,6 +232,10 @@ def ensure_created_and_migrated(self): """, "CREATE INDEX IF NOT EXISTS idx_conflicts_job_status ON conflicts(jobId, status);", ]), + ("0002_file_state_last_seen", [ + "ALTER TABLE file_state ADD COLUMN lastSeenAt TEXT NULL;", + "ALTER TABLE file_state ADD COLUMN lastSeenRunId INTEGER NULL;", + ]), ] # Apply migrations in order for name, stmts in migrations: @@ -1031,7 +1035,7 @@ def _scan_local_tree(self, root: str) -> Dict[str, Dict[str, Any]]: def _load_prev_file_state(self, job_id: int, side: str) -> Dict[str, Dict[str, Any]]: rows = self._db.query_all( - "SELECT relPath, size, mtime, isDir, deleted FROM file_state WHERE jobId=? AND side=?", + "SELECT relPath, size, mtime, isDir, deleted, deletedAt, lastSeenAt, lastSeenRunId FROM file_state WHERE jobId=? AND side=?", (job_id, side), ) prev: Dict[str, Dict[str, Any]] = {} @@ -1041,38 +1045,50 @@ def _load_prev_file_state(self, job_id: int, side: str) -> Dict[str, Dict[str, A "mtime": int(r["mtime"] or 0), "isDir": bool(r["isDir"]), "deleted": bool(r["deleted"]), + "deletedAt": r["deletedAt"], + "lastSeenAt": r["lastSeenAt"], + "lastSeenRunId": r["lastSeenRunId"], } return prev - def _persist_file_state(self, job_id: int, side: str, cur: Dict[str, Dict[str, Any]]) -> None: - """Upsert current snapshot into file_state and mark missing as deleted.""" + def _persist_file_state(self, job_id: int, side: str, cur: Dict[str, Dict[str, Any]], run_id: Optional[int]) -> None: + """Upsert current snapshot into file_state and mark missing as deleted, using lastSeenAt/lastSeenRunId.""" conn = self._db.connect() now_iso = datetime.now(timezone.utc).isoformat() with conn: - # Mark everything as deleted first; we will clear deleted for entries that exist now. - conn.execute( - "UPDATE file_state SET deleted=1, deletedAt=? WHERE jobId=? AND side=?", - (now_iso, job_id, side), - ) + # Upsert all current entries for rel, meta in cur.items(): is_dir = 1 if meta.get("isDir") else 0 size = int(meta.get("size", 0) or 0) mtime = int(meta.get("mtime", 0) or 0) - row = conn.execute( "SELECT id FROM file_state WHERE jobId=? AND side=? AND relPath=?", (job_id, side, rel), ).fetchone() if row: conn.execute( - "UPDATE file_state SET size=?, mtime=?, isDir=?, deleted=0, deletedAt=NULL WHERE jobId=? AND side=? AND relPath=?", - (size, mtime, is_dir, job_id, side, rel), + "UPDATE file_state SET size=?, mtime=?, isDir=?, deleted=0, deletedAt=NULL, lastSeenAt=?, lastSeenRunId=? WHERE jobId=? AND side=? AND relPath=?", + (size, mtime, is_dir, now_iso, run_id, job_id, side, rel), ) else: conn.execute( - "INSERT INTO file_state (jobId, side, relPath, size, mtime, isDir, deleted) VALUES (?,?,?,?,?,?,0)", - (job_id, side, rel, size, mtime, is_dir), + "INSERT INTO file_state (jobId, side, relPath, size, mtime, isDir, deleted, deletedAt, lastSeenAt, lastSeenRunId) VALUES (?,?,?,?,?,?,0,NULL,?,?)", + (job_id, side, rel, size, mtime, is_dir, now_iso, run_id), ) + # Mark missing as deleted (single UPDATE) + rels = list(cur.keys()) + if rels: + placeholders = ",".join(["?"] * len(rels)) + conn.execute( + f"UPDATE file_state SET deleted=1, deletedAt=COALESCE(deletedAt, ?) WHERE jobId=? AND side=? AND deleted=0 AND relPath NOT IN ({placeholders})", + (now_iso, job_id, side, *rels), + ) + else: + # cur is empty: mark all as deleted for this job/side + conn.execute( + "UPDATE file_state SET deleted=1, deletedAt=COALESCE(deletedAt, ?) WHERE jobId=? AND side=? AND deleted=0", + (now_iso, job_id, side), + ) def _ensure_parent_dir(self, path: str) -> None: parent = os.path.dirname(path) @@ -1166,10 +1182,6 @@ def _run_job_bidirectional(self, job: Dict[str, Any], run_id: Optional[int]) -> cur_a = self._scan_local_tree(a_root) cur_b = self._scan_local_tree(b_root) - # Persist current snapshot (baseline for next run) - self._persist_file_state(job_id, "A", cur_a) - self._persist_file_state(job_id, "B", cur_b) - def _meta_changed(cur: Dict[str, Any], prev: Dict[str, Any]) -> bool: return ( bool(cur.get("isDir")) != bool(prev.get("isDir")) @@ -1254,6 +1266,9 @@ def _winner_newest(a: Dict[str, Any], b: Dict[str, Any]) -> str: else: # propagate deletion (keep B deleted, delete A) actions_del_a.append((rel, bool(a_cur.get("isDir")))) + elif b_deleted and not allow_deletion: + # If B was deleted and deletions are not allowed, do NOT copy A->B (do nothing) + pass else: # no deletion involved: treat as create on A, copy to B actions_copy_a_to_b.append((rel, bool(a_cur.get("isDir")))) @@ -1272,6 +1287,9 @@ def _winner_newest(a: Dict[str, Any], b: Dict[str, Any]) -> str: else: # propagate deletion (keep A deleted, delete B) actions_del_b.append((rel, bool(b_cur.get("isDir")))) + elif a_deleted and not allow_deletion: + # If A was deleted and deletions are not allowed, do NOT copy B->A (do nothing) + pass else: actions_copy_b_to_a.append((rel, bool(b_cur.get("isDir")))) continue @@ -1364,6 +1382,12 @@ def _winner_newest(a: Dict[str, Any], b: Dict[str, Any]) -> str: self._log(f"[Replicator][BiDi] Execution failed: {e}", level="error") return False + # After actions, scan again and persist final file state as baseline + final_a = self._scan_local_tree(a_root) + final_b = self._scan_local_tree(b_root) + self._persist_file_state(job_id, "A", final_a, run_id) + self._persist_file_state(job_id, "B", final_b, run_id) + stats = { "copyAtoB": len(actions_copy_a_to_b), "copyBtoA": len(actions_copy_b_to_a), From 744bbbd42ede1644990a9a8e61a55443784eefb8 Mon Sep 17 00:00:00 2001 From: Louis Ouellet Date: Mon, 12 Jan 2026 20:43:29 -0500 Subject: [PATCH 07/51] BUGFIX: Addressing sync issues in bidirectional sync. --- src/replicator/replicator.py | 63 ++++++++++++++++++++++++------------ 1 file changed, 42 insertions(+), 21 deletions(-) diff --git a/src/replicator/replicator.py b/src/replicator/replicator.py index 0b4897d..73ba77c 100644 --- a/src/replicator/replicator.py +++ b/src/replicator/replicator.py @@ -300,6 +300,16 @@ def __init__(self, parent=None, job: Optional[Dict[str, Any]] = None): name_row.addWidget(self.name, 1) layout.addLayout(name_row) + # ------------------------------ + # Enabled row + # ------------------------------ + enabled_row = QHBoxLayout() + self.enabled = QCheckBox("Enabled") + self.enabled.setChecked(bool(job.get("enabled", True))) + enabled_row.addWidget(self.enabled) + enabled_row.addStretch(1) + layout.addLayout(enabled_row) + # Helper to get endpoint dict from job or fallback def _get_endpoint(key_endpoint: str, key_str: str, default_type: str = "local") -> Dict[str, Any]: ep = job.get(key_endpoint) @@ -913,6 +923,7 @@ def _extract( "preserveMetadata": bool(self.preserve_metadata.isChecked()), "mode": self.mode.currentText(), "direction": self.direction.currentText(), + "enabled": bool(self.enabled.isChecked()), } # Preserve schedule fields if they existed previously @@ -1537,9 +1548,9 @@ def init(self): root.addLayout(actions_row) # Table - self._table = QTableWidget(0, 10, self) + self._table = QTableWidget(0, 5, self) self._table.setHorizontalHeaderLabels([ - "Name", "Source", "Target", "Mode", "Direction", "Delete", "Metadata", "Schedule", "Last run", "Result" + "Name", "Enabled", "Source", "Target", "Last run", "Result" ]) self._table.setSelectionBehavior(QTableWidget.SelectRows) self._table.setEditTriggers(QTableWidget.NoEditTriggers) @@ -1618,6 +1629,7 @@ def _it(v: Any) -> QTableWidgetItem: return item self._table.setItem(r, 0, _it(job.get("name", ""))) + self._table.setItem(r, 1, _it("On" if job.get("enabled", True) else "Off")) # Source column src_str = "" src_ep = job.get("sourceEndpoint") @@ -1625,7 +1637,7 @@ def _it(v: Any) -> QTableWidgetItem: src_str = f"{src_ep.get('type', 'local')}:{src_ep.get('location', '')}" else: src_str = job.get("source", "") - self._table.setItem(r, 1, _it(src_str)) + self._table.setItem(r, 2, _it(src_str)) # Target column tgt_str = "" tgt_ep = job.get("targetEndpoint") @@ -1633,27 +1645,17 @@ def _it(v: Any) -> QTableWidgetItem: tgt_str = f"{tgt_ep.get('type', 'local')}:{tgt_ep.get('location', '')}" else: tgt_str = job.get("target", "") - self._table.setItem(r, 2, _it(tgt_str)) - self._table.setItem(r, 3, _it(job.get("mode", "mirror"))) - self._table.setItem(r, 4, _it(job.get("direction", "unidirectional"))) - self._table.setItem(r, 5, _it("Yes" if job.get("allowDeletion") else "No")) - self._table.setItem(r, 6, _it("Yes" if job.get("preserveMetadata", True) else "No")) - sched = job.get("schedule") - if not sched or not sched.get("enabled", False): - sched_str = "Off" - else: - sched_str = f"Every {sched.get('everyMinutes', 60)} min" - self._table.setItem(r, 7, _it(sched_str)) - # Last run (column 8) + self._table.setItem(r, 3, _it(tgt_str)) + # Last run (column 5) last_run = job.get("lastRun") if last_run: last_run_str = last_run else: last_run_str = "Never" - self._table.setItem(r, 8, _it(last_run_str)) - # Last result (column 9) + self._table.setItem(r, 4, _it(last_run_str)) + # Last result (column 6) last_result = job.get("lastResult", "") - self._table.setItem(r, 9, _it(last_result)) + self._table.setItem(r, 5, _it(last_result)) def _add_job(self): dlg = JobDialog(self) @@ -1752,8 +1754,15 @@ def run(self) -> bool: Returns True if all jobs succeeded. """ self._reload_jobs() - # After reloading jobs, log a verbose DB snapshot for debugging - self._db_debug_snapshot(verbose=True) + # After reloading jobs, log a DB snapshot for debugging. + # Only log verbose DB details when log.verbose is enabled. + verbose_db = False + try: + verbose_db = bool(self._configuration.get("log.verbose", False)) + except Exception: + verbose_db = False + + self._db_debug_snapshot(verbose=verbose_db) jobs = self._jobs if not jobs: self._log("[Replicator] No jobs configured.", level="warning") @@ -1761,6 +1770,9 @@ def run(self) -> bool: all_ok = True for job in jobs: + if not bool(job.get("enabled", True)): + self._log(f"[Replicator] Skipping disabled job '{job.get('name') or 'Unnamed'}'.", level="info") + continue ok = self._run_job(job) all_ok = all_ok and ok @@ -1768,6 +1780,9 @@ def run(self) -> bool: def _run_job(self, job: Dict[str, Any]) -> bool: name = job.get("name") or "Unnamed" + if not bool(job.get("enabled", True)): + self._log(f"[Replicator] Job '{name}' is disabled; skipping.", level="info") + return True job_id = job.get("id") # Determine src/dst and types src_ep = job.get("sourceEndpoint") @@ -1924,6 +1939,8 @@ def _db_fetch_jobs(self) -> List[Dict[str, Any]]: for row in rows: job = dict(row) job["id"] = row["id"] + enabled_val = row["enabled"] if "enabled" in row.keys() else 1 + job["enabled"] = bool(enabled_val) # Endpoints eps = self._db.query_all("SELECT * FROM endpoints WHERE jobId = ?", (row["id"],)) for ep in eps: @@ -1982,6 +1999,10 @@ def _db_fetch_jobs(self) -> List[Dict[str, Any]]: return jobs def _db_upsert_job(self, job: Dict[str, Any]) -> int: + + # Normalize enabled + enabled = 1 if bool(job.get("enabled", True)) else 0 + # Insert or update jobs row, endpoints, schedule (transaction) conn = self._db.connect() with conn: @@ -1992,7 +2013,7 @@ def _db_upsert_job(self, job: Dict[str, Any]) -> int: ] values = [ job.get("name"), - 1 if job.get("enabled", True) else 0, + enabled, job.get("mode", "mirror"), job.get("direction", "unidirectional"), 1 if job.get("allowDeletion", False) else 0, From c51763bd6392eddcebb7d7d3f87e1b49d5c95ae1 Mon Sep 17 00:00:00 2001 From: Louis Ouellet Date: Tue, 13 Jan 2026 07:59:59 -0500 Subject: [PATCH 08/51] BUGFIX: Addressing sync issues in bidirectional sync. --- src/replicator/replicator.py | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/src/replicator/replicator.py b/src/replicator/replicator.py index 73ba77c..4942278 100644 --- a/src/replicator/replicator.py +++ b/src/replicator/replicator.py @@ -300,16 +300,6 @@ def __init__(self, parent=None, job: Optional[Dict[str, Any]] = None): name_row.addWidget(self.name, 1) layout.addLayout(name_row) - # ------------------------------ - # Enabled row - # ------------------------------ - enabled_row = QHBoxLayout() - self.enabled = QCheckBox("Enabled") - self.enabled.setChecked(bool(job.get("enabled", True))) - enabled_row.addWidget(self.enabled) - enabled_row.addStretch(1) - layout.addLayout(enabled_row) - # Helper to get endpoint dict from job or fallback def _get_endpoint(key_endpoint: str, key_str: str, default_type: str = "local") -> Dict[str, Any]: ep = job.get(key_endpoint) @@ -447,9 +437,12 @@ def _get_endpoint(key_endpoint: str, key_str: str, default_type: str = "local") self.allow_deletion.setChecked(bool(job.get("allowDeletion", False))) self.preserve_metadata = QCheckBox("Preserve metadata") self.preserve_metadata.setChecked(bool(job.get("preserveMetadata", True))) + self.enabled = QCheckBox("Enabled") + self.enabled.setChecked(bool(job.get("enabled", True))) opts_row.addWidget(self.allow_deletion) opts_row.addSpacing(16) opts_row.addWidget(self.preserve_metadata) + opts_row.addWidget(self.enabled) opts_row.addStretch(1) layout.addLayout(opts_row) From 9b57b7e8baf5b1ad101b4ef7a5e59e7fa11248b5 Mon Sep 17 00:00:00 2001 From: Louis Ouellet Date: Tue, 13 Jan 2026 09:46:35 -0500 Subject: [PATCH 09/51] General: Created an icon for the application --- src/icons/icon.png | Bin 6287 -> 32693 bytes src/icons/icon.svg | 71 +++++++++++++++++++---------------- src/icons/logo.png | Bin 315818 -> 0 bytes src/replicator/replicator.py | 2 +- 4 files changed, 39 insertions(+), 34 deletions(-) delete mode 100644 src/icons/logo.png diff --git a/src/icons/icon.png b/src/icons/icon.png index a0d55d41223d3f67fb654d36c56d5b2c8d0484a9..e683fe20cd737303420bca99e1117a20f8dadcb2 100644 GIT binary patch literal 32693 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 ~HlrCeItIKIatl2^>SLY7n-K)tb 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 literal 6287 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 diff --git a/src/icons/icon.svg b/src/icons/icon.svg index 3be12c0..d29f626 100644 --- a/src/icons/icon.svg +++ b/src/icons/icon.svg @@ -1,46 +1,51 @@ - + - - - - + + + + - - - - + + + + - - - - + + + + - - - - - + + + + + + + + + + + - - - - - - - + + + + + + - - - - - - - - + + + + + + + + diff --git a/src/icons/logo.png b/src/icons/logo.png deleted file mode 100644 index a8bf604a23d2e8ee822a76e7e8825999c726855c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 315818 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�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

kK4B{uM_vytH_dOG^J(qw;jH7Z|Dmk5x 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

V;|+2?)H$8?~`H=w5r9C5E&w3HmxgtjLrz| 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�l1FYNeC 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 Date: Tue, 13 Jan 2026 11:31:15 -0500 Subject: [PATCH 10/51] General: Updated the README.md --- README.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 2589e5c..218151b 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ ![Version](https://img.shields.io/github/v/release/LaswitchTech/Replicator?label=Version&style=for-the-badge) ## Description -Replicator is a cross-platform Python application designed to provide replication services similar to Windows DFSR. It supports local filesystems, SMB, and FTP, making it a versatile solution for data replication needs across different operating systems. +Replicator is a cross-platform Python application designed to provide replication services similar to Windows DFSR. It supports local filesystems, SMB, FTP and SSHFS, making it a versatile solution for data replication needs across different operating systems. ## Features - **Cross-Platform Compatibility**: Replicator is compatible with Windows, macOS and Linux, with specific adjustments made to ensure seamless operation on both operating systems. @@ -30,14 +30,15 @@ Contributions to Replicator are welcome! If you have ideas for new features or h - **Submit a Pull Request**: Once your changes are ready, submit a pull request to the main repository. ## To Do - - **Support for Local**: Add support for Local filesystems. + - ~~**Support for Local**: Add support for Local filesystems.~~ - **Support for SMB**: Add support for SMB Shares. - **Support for FTP**: Add support for FTP Shares. - - **Mode Mirror**: Add replication mode Mirror. + - **Support for SSHFS**: Add support for SSH filesystems. + - ~~**Mode Mirror**: Add replication mode Mirror.~~ - **Mode Incremental**: Add replication mode Incremental. - - **Direction**: Add replication direction (One way, Two way). + - ~~**Direction**: Add replication direction (One way, Two way).~~ - **Scheduling**: Add scheduling capabilities for replication tasks. - - **Conflict Resolution**: Implement conflict resolution strategies for file changes. + - ~~**Conflict Resolution**: Implement conflict resolution strategies for file changes.~~ ## Wait, where is the documentation? Review the [Documentation](https://laswitchtech.com/en/blog/projects/pyrdpconnect/index). From ed15811e57f8a68f7bb7cf5a0b01d878374de03c Mon Sep 17 00:00:00 2001 From: Louis Ouellet Date: Tue, 13 Jan 2026 12:29:58 -0500 Subject: [PATCH 11/51] General: Updated corePY --- src/core | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core b/src/core index e656c79..683f51d 160000 --- a/src/core +++ b/src/core @@ -1 +1 @@ -Subproject commit e656c799f5a1166da593b0768a3c91f4955e369e +Subproject commit 683f51d65957faa549798662a1c884df41d09bd7 From b15570053cef25e40fde572003e079ac8da192e9 Mon Sep 17 00:00:00 2001 From: Louis Ouellet Date: Tue, 13 Jan 2026 12:37:12 -0500 Subject: [PATCH 12/51] General: Updated corePY --- src/core | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core b/src/core index 683f51d..3d09dad 160000 --- a/src/core +++ b/src/core @@ -1 +1 @@ -Subproject commit 683f51d65957faa549798662a1c884df41d09bd7 +Subproject commit 3d09dadf58ab29def4872a479f64d8f7614156bf From b0c47239b21ba12da4bc937bab137de84204de0f Mon Sep 17 00:00:00 2001 From: Louis Ouellet Date: Tue, 13 Jan 2026 12:38:00 -0500 Subject: [PATCH 13/51] General: Registered the Replicator service --- src/replicator/replicator.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/replicator/replicator.py b/src/replicator/replicator.py index 57f448f..5bbdbc7 100644 --- a/src/replicator/replicator.py +++ b/src/replicator/replicator.py @@ -1477,6 +1477,7 @@ def cli(self, cli: QApplication = None) -> None: if cli is None: return cli.add("run", "Run all replication jobs.", self.run) + cli.service.add("run", "Run all replication jobs.", self.run) # ------------------------------------------------------------------ # UI From b20d9dea9e4a29a3bf8414d0f4b5440fc9a0da50 Mon Sep 17 00:00:00 2001 From: Louis Ouellet Date: Tue, 13 Jan 2026 12:41:26 -0500 Subject: [PATCH 14/51] General: Updated corePY --- src/core | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core b/src/core index 3d09dad..0a03d19 160000 --- a/src/core +++ b/src/core @@ -1 +1 @@ -Subproject commit 3d09dadf58ab29def4872a479f64d8f7614156bf +Subproject commit 0a03d19e5aef8f3ea308fe93a897386abacd02e7 From 50e4503f08b6916710373115efaa9a0835b111f4 Mon Sep 17 00:00:00 2001 From: Louis Ouellet Date: Tue, 13 Jan 2026 12:42:23 -0500 Subject: [PATCH 15/51] General: Updated corePY --- src/core | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core b/src/core index 0a03d19..957c003 160000 --- a/src/core +++ b/src/core @@ -1 +1 @@ -Subproject commit 0a03d19e5aef8f3ea308fe93a897386abacd02e7 +Subproject commit 957c003ddfc4d0c6738eee3c51b84166d0199f38 From 5f63dffc1d2f04915a1d937ac1cb4f165ff210e0 Mon Sep 17 00:00:00 2001 From: Louis Ouellet Date: Tue, 13 Jan 2026 12:47:11 -0500 Subject: [PATCH 16/51] General: Updated corePY --- src/core | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core b/src/core index 957c003..2223329 160000 --- a/src/core +++ b/src/core @@ -1 +1 @@ -Subproject commit 957c003ddfc4d0c6738eee3c51b84166d0199f38 +Subproject commit 2223329880b4d9cd8704ab2c7b9efc7dceef12e1 From 318f8a165b0bcd66a296154a75c3a4f144bffbd4 Mon Sep 17 00:00:00 2001 From: Louis Ouellet Date: Tue, 13 Jan 2026 12:50:23 -0500 Subject: [PATCH 17/51] General: Updated corePY --- src/core | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core b/src/core index 2223329..120c5da 160000 --- a/src/core +++ b/src/core @@ -1 +1 @@ -Subproject commit 2223329880b4d9cd8704ab2c7b9efc7dceef12e1 +Subproject commit 120c5da0e622c08b4cb6c273e9d97471701f22f5 From 0a7fdda3aadc5bbcca9f8d428e64318622e42133 Mon Sep 17 00:00:00 2001 From: Louis Ouellet Date: Tue, 13 Jan 2026 13:29:52 -0500 Subject: [PATCH 18/51] General: Updated README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 218151b..7af9d70 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,7 @@ Contributions to Replicator are welcome! If you have ideas for new features or h - ~~**Direction**: Add replication direction (One way, Two way).~~ - **Scheduling**: Add scheduling capabilities for replication tasks. - ~~**Conflict Resolution**: Implement conflict resolution strategies for file changes.~~ + - ~~**Service Integration**: Integrate with system services for background operation.~~ ## Wait, where is the documentation? Review the [Documentation](https://laswitchtech.com/en/blog/projects/pyrdpconnect/index). From 49a8ed7e1bdac01afa3d311862f304c5a8336e67 Mon Sep 17 00:00:00 2001 From: Louis Ouellet Date: Tue, 13 Jan 2026 13:30:40 -0500 Subject: [PATCH 19/51] BUGFIX: Attempting to Stops the DB from growing uncontrollably --- src/replicator/replicator.py | 164 +++++++++++++++++++++++++++++++++-- 1 file changed, 159 insertions(+), 5 deletions(-) diff --git a/src/replicator/replicator.py b/src/replicator/replicator.py index 5bbdbc7..249999b 100644 --- a/src/replicator/replicator.py +++ b/src/replicator/replicator.py @@ -87,6 +87,21 @@ def query_one(self, sql: str, params: tuple = ()) -> Optional[sqlite3.Row]: cur = conn.execute(sql, params) return cur.fetchone() + def get_meta(self, key: str, default: Optional[str] = None) -> Optional[str]: + row = self.query_one("SELECT value FROM meta WHERE key = ?", (key,)) + if not row: + return default + return row["value"] + + def set_meta(self, key: str, value: Optional[str]) -> None: + conn = self.connect() + with conn: + row = conn.execute("SELECT 1 FROM meta WHERE key = ?", (key,)).fetchone() + if row: + conn.execute("UPDATE meta SET value = ? WHERE key = ?", (value, key)) + else: + conn.execute("INSERT INTO meta (key, value) VALUES (?, ?)", (key, value)) + def ensure_created_and_migrated(self): # Ensure parent dir exists db_dir = os.path.dirname(self._db_path) @@ -236,6 +251,16 @@ def ensure_created_and_migrated(self): "ALTER TABLE file_state ADD COLUMN lastSeenAt TEXT NULL;", "ALTER TABLE file_state ADD COLUMN lastSeenRunId INTEGER NULL;", ]), + ("0003_meta_kv", [ + """ + CREATE TABLE IF NOT EXISTS meta ( + key TEXT PRIMARY KEY, + created DATETIME DEFAULT CURRENT_TIMESTAMP, + modified DATETIME DEFAULT CURRENT_TIMESTAMP, + value TEXT NULL + ); + """, + ]), ] # Apply migrations in order for name, stmts in migrations: @@ -1139,6 +1164,13 @@ def _record_conflict( note: str = "", ) -> None: try: + # Avoid inserting duplicate open conflicts for the same path/note. + existing = self._db.query_one( + "SELECT id FROM conflicts WHERE jobId=? AND relPath=? AND status='open' AND COALESCE(note,'')=COALESCE(?, '') ORDER BY id DESC LIMIT 1", + (job_id, rel, note or ""), + ) + if existing: + return self._db.execute( """ INSERT INTO conflicts (jobId, runId, relPath, a_size, a_mtime, a_hash, b_size, b_mtime, b_hash, status, note) @@ -1160,7 +1192,7 @@ def _record_conflict( except Exception as e: self._log(f"[Replicator][DB] Failed to record conflict for '{rel}': {e}", level="warning") - def _run_job_bidirectional(self, job: Dict[str, Any], run_id: Optional[int]) -> bool: + def _run_job_bidirectional(self, job: Dict[str, Any], run_id: Optional[int]) -> tuple[bool, Dict[str, Any]]: """Bidirectional sync using `file_state` as the persistent baseline. Currently implemented for local<->local only. @@ -1384,7 +1416,7 @@ def _winner_newest(a: Dict[str, Any], b: Dict[str, Any]) -> str: except Exception as e: self._log(f"[Replicator][BiDi] Execution failed: {e}", level="error") - return False + return False, {} # After actions, scan again and persist final file state as baseline final_a = self._scan_local_tree(a_root) @@ -1414,7 +1446,7 @@ def _winner_newest(a: Dict[str, Any], b: Dict[str, Any]) -> str: except Exception: pass - return True + return True, stats """ Replicator UI + CLI entrypoint. Jobs are stored in SQLite database (see ReplicatorDB). @@ -1542,7 +1574,7 @@ def init(self): root.addLayout(actions_row) # Table - self._table = QTableWidget(0, 5, self) + self._table = QTableWidget(0, 6, self) self._table.setHorizontalHeaderLabels([ "Name", "Enabled", "Source", "Target", "Last run", "Result" ]) @@ -1770,6 +1802,23 @@ def run(self) -> bool: ok = self._run_job(job) all_ok = all_ok and ok + # Run DB maintenance at most once per day. + try: + last = self._db.get_meta("maintenance.lastRunAt") + should = True + if last: + try: + last_dt = datetime.fromisoformat(str(last)) + if last_dt.tzinfo is None: + last_dt = last_dt.replace(tzinfo=timezone.utc) + should = (datetime.now(timezone.utc) - last_dt).total_seconds() >= 86400 + except Exception: + should = True + if should: + self._db_maintenance() + except Exception: + pass + return all_ok def _run_job(self, job: Dict[str, Any]) -> bool: @@ -1817,6 +1866,7 @@ def _run_job(self, job: Dict[str, Any]) -> bool: # Insert a run record at start started_at = datetime.now(timezone.utc).isoformat() run_id = None + bidi_stats = None try: cur = self._db.execute( "INSERT INTO runs (jobId, startedAt, result) VALUES (?, ?, ?)", @@ -1826,10 +1876,12 @@ def _run_job(self, job: Dict[str, Any]) -> bool: except Exception as e: self._log(f"[Replicator] Failed to insert run record: {e}", level="warning") + record_noop_runs = bool(self._configuration.get("db.recordNoopRuns", False)) + ok = False try: if str(direction).lower() == "bidirectional": - ok = self._run_job_bidirectional(job, run_id) + ok, bidi_stats = self._run_job_bidirectional(job, run_id) else: ok = self._fs.copy( src, @@ -1844,6 +1896,25 @@ def _run_job(self, job: Dict[str, Any]) -> bool: self._log(f"[Replicator] Job '{name}' failed: {e}", level="error") ok = False + # If this was a bidirectional run with no actions/changes, optionally drop the run row. + if run_id and bidi_stats is not None and not record_noop_runs: + try: + noop = ( + int(bidi_stats.get("copyAtoB", 0) or 0) == 0 + and int(bidi_stats.get("copyBtoA", 0) or 0) == 0 + and int(bidi_stats.get("delA", 0) or 0) == 0 + and int(bidi_stats.get("delB", 0) or 0) == 0 + and int(bidi_stats.get("changedA", 0) or 0) == 0 + and int(bidi_stats.get("changedB", 0) or 0) == 0 + and int(bidi_stats.get("deletedA", 0) or 0) == 0 + and int(bidi_stats.get("deletedB", 0) or 0) == 0 + ) + if noop: + self._db.execute("DELETE FROM runs WHERE id = ?", (run_id,)) + run_id = None + except Exception: + pass + # Persist lastRun and lastResult, refresh UI, save to DB now_str = datetime.now(timezone.utc).isoformat() job["lastRun"] = now_str @@ -2118,3 +2189,86 @@ def _db_upsert_job(self, job: Dict[str, Any]) -> int: def _db_delete_job(self, job_id: int) -> None: self._db.execute("DELETE FROM jobs WHERE id = ?", (job_id,)) + + def _db_maintenance(self) -> None: + """Prune old history and run lightweight SQLite maintenance. + + Safe defaults: + - Keep last N runs per job (db.retention.runs.keepLast) + - Also prune runs older than X days (db.retention.runs.keepDays) + - Keep open conflicts; prune resolved conflicts older than X days + - Optionally VACUUM (off by default) + + This is designed to be called periodically (e.g. daily) by the service loop. + """ + try: + keep_last = int(self._configuration.get("db.retention.runs.keepLast", 500)) + keep_days = int(self._configuration.get("db.retention.runs.keepDays", 30)) + prune_conflict_days = int(self._configuration.get("db.retention.conflicts.keepDays", 90)) + prune_deleted_state_days = int(self._configuration.get("db.retention.fileState.deletedKeepDays", 30)) + do_vacuum = bool(self._configuration.get("db.maintenance.vacuum", False)) + except Exception: + keep_last, keep_days = 500, 30 + prune_conflict_days, prune_deleted_state_days = 90, 30 + do_vacuum = False + + conn = self._db.connect() + now = datetime.now(timezone.utc) + now_iso = now.isoformat() + + # Runs: keep last N per job + prune anything older than keep_days + try: + job_ids = [r[0] for r in conn.execute("SELECT id FROM jobs").fetchall()] + with conn: + for jid in job_ids: + if keep_last > 0: + conn.execute( + """ + DELETE FROM runs + WHERE jobId = ? + AND id NOT IN ( + SELECT id FROM runs WHERE jobId = ? ORDER BY id DESC LIMIT ? + ) + """, + (jid, jid, keep_last), + ) + + if keep_days > 0: + conn.execute( + "DELETE FROM runs WHERE julianday(created) < julianday('now', ?) ", + (f'-{keep_days} days',), + ) + except Exception as e: + self._log(f"[Replicator][DB] Maintenance: runs prune failed: {e}", level="warning") + + # Conflicts: keep all open; prune non-open older than prune_conflict_days + try: + if prune_conflict_days > 0: + with conn: + conn.execute( + "DELETE FROM conflicts WHERE status <> 'open' AND julianday(created) < julianday('now', ?) ", + (f'-{prune_conflict_days} days',), + ) + except Exception as e: + self._log(f"[Replicator][DB] Maintenance: conflicts prune failed: {e}", level="warning") + + # file_state: prune deleted entries that have been deleted for a long time + try: + if prune_deleted_state_days > 0: + with conn: + conn.execute( + "DELETE FROM file_state WHERE deleted = 1 AND deletedAt IS NOT NULL AND julianday(deletedAt) < julianday('now', ?) ", + (f'-{prune_deleted_state_days} days',), + ) + except Exception as e: + self._log(f"[Replicator][DB] Maintenance: file_state prune failed: {e}", level="warning") + + # SQLite maintenance: optimize; optionally vacuum + try: + conn.execute("PRAGMA optimize;") + if do_vacuum: + conn.execute("VACUUM;") + self._db.set_meta("maintenance.lastRunAt", now_iso) + self._log("[Replicator][DB] Maintenance completed.", level="debug") + except Exception as e: + self._log(f"[Replicator][DB] Maintenance failed: {e}", level="warning") From 71f0a2117334dedcadb23cf34a68db8829b73a43 Mon Sep 17 00:00:00 2001 From: Louis Ouellet Date: Wed, 14 Jan 2026 16:47:44 -0500 Subject: [PATCH 20/51] General: Spliting code in more manageable chunks --- src/replicator/__init__.py | 9 + src/replicator/job.py | 848 +++++++++++++++++++++++++++++++++++ src/replicator/migration.py | 260 +++++++++++ src/replicator/replicator.py | 722 +---------------------------- src/replicator/ui.py | 737 ++++++++++++++++++++++++++++++ 5 files changed, 1859 insertions(+), 717 deletions(-) create mode 100644 src/replicator/__init__.py create mode 100644 src/replicator/job.py create mode 100644 src/replicator/migration.py create mode 100644 src/replicator/ui.py diff --git a/src/replicator/__init__.py b/src/replicator/__init__.py new file mode 100644 index 0000000..b06ab96 --- /dev/null +++ b/src/replicator/__init__.py @@ -0,0 +1,9 @@ +#!/usr/bin/env python3 +# src/core/__init__.py + +from .replicator import Replicator +from .ui import JobDialog, ScheduleDialog + +__version__ = "1.0.0" + +__all__ = ["Replicator", "JobDialog", "ScheduleDialog"] diff --git a/src/replicator/job.py b/src/replicator/job.py new file mode 100644 index 0000000..0790da8 --- /dev/null +++ b/src/replicator/job.py @@ -0,0 +1,848 @@ +#!/usr/bin/env python3 +# src/replicator/job.py + +from __future__ import annotations + +from dataclasses import dataclass, field +from datetime import datetime, timedelta, timezone, time +from typing import Any, Callable, Dict, Iterable, List, Mapping, Optional, Tuple, Union +import json +import sqlite3 +try: + # corePY SQLite wrapper (preferred) + from core.database.sqlite import SQLite # type: ignore +except Exception: # pragma: no cover + SQLite = None # type: ignore + + +JsonDict = Dict[str, Any] + + +# --------------------------------------------------------------------------- +# Data models +# --------------------------------------------------------------------------- + +@dataclass(frozen=True) +class Endpoint: + """ + UI-agnostic endpoint description. + + Compatible with current DB schema in replicator.py: + - endpoints table: role, type, location, port, guest, username, password, useKey, sshKey, options + - Job stores endpoints as: + {"type": "...", "location": "...", "auth": {...}} + """ + type: str = "local" + location: str = "" + auth: JsonDict = field(default_factory=dict) + + def validate(self, role: str) -> List[str]: + errs: List[str] = [] + if not self.type: + errs.append(f"{role} endpoint type is required.") + if not self.location: + errs.append(f"{role} endpoint location is required.") + t = (self.type or "").lower() + + # Basic sanity checks only (no network IO, no UI) + if t in ("ftp", "ssh"): + port = self.auth.get("port") + if port is not None: + try: + p = int(port) + if p <= 0 or p > 65535: + errs.append(f"{role} endpoint port must be between 1 and 65535.") + except Exception: + errs.append(f"{role} endpoint port must be an integer.") + return errs + + def to_db_fields(self, role: str) -> JsonDict: + """ + Convert endpoint into endpoints-table fields. + Keeps compatibility with your current schema/logic: + - known auth keys are stored as columns + - extra auth keys are JSON in 'options' + """ + t = (self.type or "local").lower() + auth = dict(self.auth or {}) + + port: Optional[int] = None + guest: int = 1 + username: Optional[str] = None + password: Optional[str] = None + useKey: int = 0 + sshKey: Optional[str] = None + options: JsonDict = {} + + if t == "local": + pass + elif t in ("smb", "ftp"): + guest = 1 if bool(auth.get("guest", True)) else 0 + username = auth.get("username") or None + password = auth.get("password") or None + if t == "ftp": + try: + port = int(auth.get("port", 21)) + except Exception: + port = 21 + elif t == "ssh": + useKey = 1 if bool(auth.get("useKey", False)) else 0 + username = auth.get("username") or None + password = auth.get("password") or None + try: + port = int(auth.get("port", 22)) + except Exception: + port = 22 + sshKey = auth.get("key") or None + + known_keys = {"guest", "username", "password", "port", "useKey", "key"} + for k, v in auth.items(): + if k not in known_keys: + options[k] = v + + return { + "role": role, + "type": t, + "location": self.location, + "port": port, + "guest": guest, + "username": username, + "password": password, + "useKey": useKey, + "sshKey": sshKey, + "options": json.dumps(options) if options else None, + } + + @staticmethod + def from_db_row(row: Mapping[str, Any]) -> "Endpoint": + """ + Parse endpoints table row into Endpoint. + """ + t = (row.get("type") or "local").lower() + auth: JsonDict = {} + + if t == "local": + auth = {} + elif t in ("smb", "ftp"): + auth = { + "guest": bool(row.get("guest", 1)), + "username": row.get("username") or "", + "password": row.get("password") or "", + } + if t == "ftp": + auth["port"] = row.get("port") or 21 + elif t == "ssh": + auth = { + "useKey": bool(row.get("useKey", 0)), + "username": row.get("username") or "", + "password": row.get("password") or "", + "port": row.get("port") or 22, + "key": row.get("sshKey") or "", + } + + opt = row.get("options") + if opt: + try: + extra = json.loads(opt) + if isinstance(extra, dict): + auth.update(extra) + except Exception: + pass + + return Endpoint(type=t, location=row.get("location") or "", auth=auth) + + +@dataclass(frozen=True) +class Schedule: + """ + Job schedule config. + + Current DB has: + - enabled + - everyMinutes + - nextRunAt + - lastScheduledRunAt + + Upcoming feature: day/time windows. + - windows: dict weekday -> list of {start:"HH:MM", end:"HH:MM"} + weekday: 0=Mon ... 6=Sun (datetime.weekday()). + + Note: windows are not persisted yet by default; but we keep structure ready. + """ + enabled: bool = False + everyMinutes: int = 60 + + # Optional advanced scheduling windows: + # {0:[{"start":"22:00","end":"06:00"}], 6:[{"start":"00:00","end":"23:59"}], ...} + windows: JsonDict = field(default_factory=dict) + + # Optional DB fields (if you want to use them later) + nextRunAt: Optional[str] = None + lastScheduledRunAt: Optional[str] = None + + def validate(self) -> List[str]: + errs: List[str] = [] + if self.enabled: + if self.everyMinutes <= 0: + errs.append("Schedule everyMinutes must be > 0 when schedule is enabled.") + # windows validation is tolerant (future UI can enforce strictness) + if self.windows and not isinstance(self.windows, dict): + errs.append("Schedule windows must be a dict of weekday -> list[window].") + return errs + + def _parse_hhmm(self, s: str) -> Optional[time]: + try: + parts = s.strip().split(":") + if len(parts) != 2: + return None + hh = int(parts[0]) + mm = int(parts[1]) + if hh < 0 or hh > 23 or mm < 0 or mm > 59: + return None + return time(hour=hh, minute=mm) + except Exception: + return None + + def _in_window_for_day(self, dt: datetime) -> bool: + """ + Returns True if dt is within any allowed window for dt.weekday(). + Supports overnight windows like 22:00-06:00. + If windows is empty => always allowed. + """ + if not self.windows: + return True + + wd = dt.weekday() # 0..6 + day_windows = self.windows.get(str(wd)) or self.windows.get(wd) + if not day_windows: + return False + + tnow = dt.timetz().replace(tzinfo=None) + + for w in day_windows: + if not isinstance(w, dict): + continue + s = w.get("start") + e = w.get("end") + if not s or not e: + continue + ts = self._parse_hhmm(str(s)) + te = self._parse_hhmm(str(e)) + if ts is None or te is None: + continue + + if ts <= te: + # same-day window + if ts <= tnow <= te: + return True + else: + # overnight window (e.g., 22:00 -> 06:00) + if tnow >= ts or tnow <= te: + return True + + return False + + def should_run_now(self, now: Optional[datetime] = None) -> bool: + if not self.enabled: + return False + now = now or datetime.now(timezone.utc) + return self._in_window_for_day(now) + + def next_run_at(self, now: Optional[datetime] = None) -> Optional[datetime]: + """ + Returns the next datetime the job is allowed to run based on: + - enabled + - everyMinutes + - windows (if any) + + Strategy: + - Start from next minute tick. + - Scan forward in 1-minute steps up to 7 days. + - Pick first time that is within an allowed window AND aligns to interval. + + This is deterministic and fast enough (<= 10080 iterations). + """ + if not self.enabled: + return None + if self.everyMinutes <= 0: + # Interval 0 is "continuous" in your earlier convo; for scheduling we treat it as "run now" + return now or datetime.now(timezone.utc) + + now = now or datetime.now(timezone.utc) + # Normalize to next minute (zero seconds/micros) + cur = now.replace(second=0, microsecond=0) + timedelta(minutes=1) + + # Align to interval relative to epoch minute 0 (UTC) + def aligned(dt: datetime) -> bool: + epoch = datetime(1970, 1, 1, tzinfo=timezone.utc) + minutes = int((dt - epoch).total_seconds() // 60) + return (minutes % int(self.everyMinutes)) == 0 + + limit = cur + timedelta(days=7) + while cur <= limit: + if aligned(cur) and self._in_window_for_day(cur): + return cur + cur += timedelta(minutes=1) + + return None + + +@dataclass +class JobRunResult: + ok: bool + started_at: str + ended_at: str + result: str # "ok"|"fail"|"running" + message: Optional[str] = None + stats: JsonDict = field(default_factory=dict) + + +@dataclass +class Job: + """ + UI-agnostic domain object. + + Owns: + - id, name, enabled, mode, direction, allowDeletion, preserveMetadata, conflictPolicy, pairId + - sourceEndpoint, targetEndpoint + - schedule + - lastRun, lastResult, lastError + + Does NOT own: + - dialogs/widgets + - MsgBox + - DB connections/cursors + """ + + id: Optional[int] = None + name: str = "" + enabled: bool = True + mode: str = "mirror" + direction: str = "unidirectional" + allowDeletion: bool = False + preserveMetadata: bool = True + conflictPolicy: str = "newest" + pairId: Optional[str] = None + + sourceEndpoint: Endpoint = field(default_factory=Endpoint) + targetEndpoint: Endpoint = field(default_factory=Endpoint) + + schedule: Schedule = field(default_factory=Schedule) + + lastRun: Optional[str] = None + lastResult: Optional[str] = None + lastError: Optional[str] = None + + # ------------------------------------------------------------------ + # Validation & scheduling + # ------------------------------------------------------------------ + + def validate(self) -> List[str]: + errs: List[str] = [] + if not self.name.strip(): + errs.append("Job name is required.") + if not self.mode: + errs.append("Job mode is required.") + if not self.direction: + errs.append("Job direction is required.") + + errs.extend(self.sourceEndpoint.validate("source")) + errs.extend(self.targetEndpoint.validate("target")) + errs.extend(self.schedule.validate()) + + # Basic constraints + d = (self.direction or "").lower() + if d not in ("unidirectional", "bidirectional"): + errs.append("Job direction must be 'unidirectional' or 'bidirectional'.") + + cp = (self.conflictPolicy or "newest").lower() + if cp not in ("newest", "keepa", "a", "keepb", "b"): + errs.append("conflictPolicy must be one of: newest, keepA/a, keepB/b.") + + return errs + + def should_run_now(self, now: Optional[datetime] = None) -> bool: + if not self.enabled: + return False + return self.schedule.should_run_now(now) + + def next_run_at(self, now: Optional[datetime] = None) -> Optional[datetime]: + if not self.enabled: + return None + return self.schedule.next_run_at(now) + + # ------------------------------------------------------------------ + # Execution (delegated) + # ------------------------------------------------------------------ + + def run( + self, + *, + now: Optional[datetime] = None, + copy_func: Optional[Callable[..., bool]] = None, + bidirectional_func: Optional[Callable[..., Tuple[bool, JsonDict]]] = None, + logger: Optional[Callable[[str, str], None]] = None, + ) -> JobRunResult: + """ + Execute job using injected runtime functions. + + - copy_func: should perform unidirectional copy, signature compatible with: + copy_func(src, dst, preserve_metadata=bool, allow_deletion=bool) -> bool + (matches your FileSystem.copy usage) + - bidirectional_func: should perform bidi sync, signature: + bidirectional_func(job: Job, run_id: Optional[int]) -> (ok, stats) + NOTE: run_id is NOT managed here; JobRunner should pass run_id. + - logger: (message, level) callback; level examples: "debug","info","warning","error" + + Returns JobRunResult with ok/fail + stats + timestamps. + """ + now_dt = now or datetime.now(timezone.utc) + started_at = now_dt.isoformat() + + def _log(msg: str, level: str = "info") -> None: + if logger: + try: + logger(msg, level) + except Exception: + pass + + # Validate first (no UI) + errs = self.validate() + if errs: + msg = "; ".join(errs) + ended = datetime.now(timezone.utc).isoformat() + self.lastRun = ended + self.lastResult = "fail" + self.lastError = msg + return JobRunResult( + ok=False, + started_at=started_at, + ended_at=ended, + result="fail", + message=msg, + stats={}, + ) + + if not self.enabled: + ended = datetime.now(timezone.utc).isoformat() + return JobRunResult( + ok=True, + started_at=started_at, + ended_at=ended, + result="ok", + message="Job disabled; skipped.", + stats={}, + ) + + src = self.sourceEndpoint.location + dst = self.targetEndpoint.location + preserve = bool(self.preserveMetadata) + allow_del = bool(self.allowDeletion) + + ok: bool = False + stats: JsonDict = {} + + try: + if (self.direction or "").lower() == "bidirectional": + if not bidirectional_func: + raise NotImplementedError("Bidirectional engine not provided.") + _log(f"[Job] Running bidirectional job '{self.name}': {src} <-> {dst}", "info") + ok, stats = bidirectional_func(self, None) + else: + if not copy_func: + raise NotImplementedError("Copy function not provided.") + _log(f"[Job] Running unidirectional job '{self.name}': {src} -> {dst}", "info") + ok = bool(copy_func(src, dst, preserve_metadata=preserve, allow_deletion=allow_del)) + except NotImplementedError as e: + ok = False + self.lastError = str(e) + _log(f"[Job] Not supported: {e}", "error") + except Exception as e: + ok = False + self.lastError = str(e) + _log(f"[Job] Execution failed: {e}", "error") + + ended_at = datetime.now(timezone.utc).isoformat() + self.lastRun = ended_at + self.lastResult = "ok" if ok else "fail" + if ok: + self.lastError = None + else: + self.lastError = self.lastError or "Failed" + + return JobRunResult( + ok=ok, + started_at=started_at, + ended_at=ended_at, + result="ok" if ok else "fail", + message=None if ok else self.lastError, + stats=stats or {}, + ) + + # ------------------------------------------------------------------ + # Serialization helpers + # ------------------------------------------------------------------ + + def to_row_dicts(self) -> Dict[str, Any]: + """ + Serialize into DB-ready row dicts: + - job_row: fields for jobs table + - endpoint_rows: list of fields for endpoints table (excluding jobId) + - schedule_row: fields for schedule table (excluding jobId) + + JobStore/Runner can add jobId and persist. + """ + job_row = { + "id": self.id, + "name": self.name, + "enabled": 1 if self.enabled else 0, + "mode": self.mode or "mirror", + "direction": self.direction or "unidirectional", + "allowDeletion": 1 if self.allowDeletion else 0, + "preserveMetadata": 1 if self.preserveMetadata else 0, + "pairId": self.pairId, + "conflictPolicy": self.conflictPolicy or "newest", + "lastRun": self.lastRun, + "lastResult": self.lastResult, + "lastError": self.lastError, + } + + endpoint_rows = [ + self.sourceEndpoint.to_db_fields("source"), + self.targetEndpoint.to_db_fields("target"), + ] + + sched_row = { + "enabled": 1 if self.schedule.enabled else 0, + "everyMinutes": int(self.schedule.everyMinutes), + # nextRunAt / lastScheduledRunAt can be managed by JobRunner + "nextRunAt": self.schedule.nextRunAt, + "lastScheduledRunAt": self.schedule.lastScheduledRunAt, + # windows not persisted yet by default + # "windows": json.dumps(self.schedule.windows) ... + } + + return { + "job_row": job_row, + "endpoint_rows": endpoint_rows, + "schedule_row": sched_row, + } + + @staticmethod + def from_db_rows( + job_row: Mapping[str, Any], + endpoint_rows: Iterable[Mapping[str, Any]], + schedule_row: Optional[Mapping[str, Any]] = None, + ) -> "Job": + """ + Build Job from DB rows (jobs + endpoints + schedule). + + This matches your current schema and your current in-memory shape. + """ + j = Job( + id=int(job_row.get("id")) if job_row.get("id") is not None else None, + name=str(job_row.get("name") or ""), + enabled=bool(job_row.get("enabled", 1)), + mode=str(job_row.get("mode") or "mirror"), + direction=str(job_row.get("direction") or "unidirectional"), + allowDeletion=bool(job_row.get("allowDeletion", 0)), + preserveMetadata=bool(job_row.get("preserveMetadata", 1)), + conflictPolicy=str(job_row.get("conflictPolicy") or "newest"), + pairId=job_row.get("pairId"), + lastRun=job_row.get("lastRun"), + lastResult=job_row.get("lastResult"), + lastError=job_row.get("lastError"), + ) + + # Endpoints by role + src = None + tgt = None + for r in endpoint_rows: + role = (r.get("role") or "").lower() + ep = Endpoint.from_db_row(r) + if role == "source": + src = ep + elif role == "target": + tgt = ep + + j.sourceEndpoint = src or Endpoint() + j.targetEndpoint = tgt or Endpoint() + + # Schedule + if schedule_row: + windows: JsonDict = {} + # If you later add a windows column, you can parse it here. + # raw_windows = schedule_row.get("windows") + # if raw_windows: ... + j.schedule = Schedule( + enabled=bool(schedule_row.get("enabled", 0)), + everyMinutes=int(schedule_row.get("everyMinutes", 60) or 60), + windows=windows, + nextRunAt=schedule_row.get("nextRunAt"), + lastScheduledRunAt=schedule_row.get("lastScheduledRunAt"), + ) + else: + j.schedule = Schedule(enabled=False, everyMinutes=60) + + return j + + # Convenience: current in-memory dict shape compatibility + def to_legacy_dict(self) -> JsonDict: + """ + Produce the same job dict shape your current Replicator code expects. + Useful as an interim bridge while refactoring. + """ + return { + "id": self.id, + "name": self.name, + "enabled": self.enabled, + "mode": self.mode, + "direction": self.direction, + "allowDeletion": self.allowDeletion, + "preserveMetadata": self.preserveMetadata, + "pairId": self.pairId, + "conflictPolicy": self.conflictPolicy, + "sourceEndpoint": {"type": self.sourceEndpoint.type, "location": self.sourceEndpoint.location, "auth": dict(self.sourceEndpoint.auth)}, + "targetEndpoint": {"type": self.targetEndpoint.type, "location": self.targetEndpoint.location, "auth": dict(self.targetEndpoint.auth)}, + "schedule": { + "enabled": self.schedule.enabled, + "everyMinutes": self.schedule.everyMinutes, + # windows not exposed yet by current UI + }, + "lastRun": self.lastRun, + "lastResult": self.lastResult, + "lastError": self.lastError, + } + + + +class JobStore: + """SQLite persistence for Job domain objects. + + This class is UI-agnostic and contains no Qt dependencies. + + It targets the schema currently defined in `replicator.py`: + - jobs + - endpoints + - schedule + + Notes: + - This store does not write runs/conflicts/file_state. + - It upserts endpoints by (jobId, role) and schedule by (jobId). + """ + + def __init__(self, db: Any): + """Create a JobStore. + + Preferred dependency is `core.database.sqlite.SQLite` (wrapper). + + Accepts either: + - a `SQLite` instance (preferred) + - a `sqlite3.Connection` + - a callable that returns `sqlite3.Connection` + + The fallback connection path exists only for backwards compatibility. + """ + self._db = db + + # ------------------------------- + # Connection helpers + # ------------------------------- + + def _is_core_sqlite(self) -> bool: + # Avoid importing Qt here; we only check for the wrapper API. + return SQLite is not None and isinstance(self._db, SQLite) + + def _conn(self) -> sqlite3.Connection: + """Fallback: get raw sqlite3.Connection.""" + if isinstance(self._db, sqlite3.Connection): + return self._db + if callable(self._db): + c = self._db() + if not isinstance(c, sqlite3.Connection): + raise TypeError("JobStore connection provider must return sqlite3.Connection") + return c + raise TypeError("JobStore requires core.database.sqlite.SQLite or sqlite3.Connection") + + def _select(self, table: str, where: Optional[str] = None, params: Any = None, *, order_by: Optional[str] = None) -> List[Dict[str, Any]]: + if self._is_core_sqlite(): + return self._db.select(table, where=where, params=params, order_by=order_by) # type: ignore[union-attr] + conn = self._conn() + sql = f'SELECT * FROM "{table}"' + if where: + sql += f" WHERE {where}" + if order_by: + sql += f" ORDER BY {order_by}" + sql += ";" + cur = conn.execute(sql, params or ()) + rows = cur.fetchall() + return [dict(r) for r in rows] + + def _one(self, table: str, where: str, params: Any) -> Optional[Dict[str, Any]]: + if self._is_core_sqlite(): + return self._db.one(f'SELECT * FROM "{table}" WHERE {where} LIMIT 1;', params) # type: ignore[union-attr] + conn = self._conn() + cur = conn.execute(f'SELECT * FROM "{table}" WHERE {where} LIMIT 1;', params) + r = cur.fetchone() + return dict(r) if r else None + + def _insert(self, table: str, data: Dict[str, Any]) -> int: + if self._is_core_sqlite(): + return int(self._db.insert(table, data)) # type: ignore[union-attr] + conn = self._conn() + keys = list(data.keys()) + cols = ", ".join([f'"{k}"' for k in keys]) + placeholders = ", ".join(["?" for _ in keys]) + sql = f'INSERT INTO "{table}" ({cols}) VALUES ({placeholders});' + cur = conn.execute(sql, tuple(data[k] for k in keys)) + return int(cur.lastrowid or 0) + + def _update(self, table: str, data: Dict[str, Any], where: str, params: Any) -> None: + if self._is_core_sqlite(): + # wrapper uses named binding for update WHERE params + if not isinstance(params, dict): + raise ValueError("JobStore._update with core SQLite requires dict params") + self._db.update(table, data, where, params) # type: ignore[union-attr] + return + conn = self._conn() + keys = list(data.keys()) + set_clause = ", ".join([f'"{k}"=?' for k in keys]) + sql = f'UPDATE "{table}" SET {set_clause} WHERE {where};' + conn.execute(sql, tuple(data[k] for k in keys) + tuple(params if isinstance(params, tuple) else ())) + + def _upsert(self, table: str, data: Dict[str, Any], conflict_columns: List[str], update_columns: Optional[List[str]] = None) -> None: + if self._is_core_sqlite(): + self._db.upsert(table, data, conflict_columns, update_columns) # type: ignore[union-attr] + return + # Fallback path: build an INSERT..ON CONFLICT statement. + keys = list(data.keys()) + cols = ", ".join([f'"{k}"' for k in keys]) + placeholders = ", ".join(["?" for _ in keys]) + conflict = ", ".join([f'"{c}"' for c in conflict_columns]) + if update_columns is None: + update_columns = [k for k in keys if k not in conflict_columns] + if update_columns: + set_clause = ", ".join([f'"{k}"=excluded."{k}"' for k in update_columns]) + sql = f'INSERT INTO "{table}" ({cols}) VALUES ({placeholders}) ON CONFLICT({conflict}) DO UPDATE SET {set_clause};' + else: + sql = f'INSERT INTO "{table}" ({cols}) VALUES ({placeholders}) ON CONFLICT({conflict}) DO NOTHING;' + conn = self._conn() + conn.execute(sql, tuple(data[k] for k in keys)) + + def _delete(self, table: str, where: str, params: Any) -> None: + if self._is_core_sqlite(): + self._db.delete(table, where, params) # type: ignore[union-attr] + return + conn = self._conn() + conn.execute(f'DELETE FROM "{table}" WHERE {where};', params) + + def _transaction(self): + if self._is_core_sqlite(): + return self._db.transaction() # type: ignore[union-attr] + # sqlite3.Connection supports context manager transactions + return self._conn() + + # ------------------------------- + # Reads + # ------------------------------- + + def fetch_all(self) -> List[Job]: + job_rows = self._select("jobs", order_by="id ASC") + jobs: List[Job] = [] + for jr in job_rows: + jid = int(jr.get("id") or 0) + ep_rows = self.fetch_endpoints_rows(jid) + sched_row = self.fetch_schedule_row(jid) + jobs.append(Job.from_db_rows(jr, ep_rows, sched_row)) + return jobs + + def fetch_by_id(self, job_id: int) -> Optional[Job]: + jr = self._one("jobs", "id = ?", (int(job_id),)) + if not jr: + return None + ep_rows = self.fetch_endpoints_rows(int(job_id)) + sched_row = self.fetch_schedule_row(int(job_id)) + return Job.from_db_rows(jr, ep_rows, sched_row) + + def fetch_endpoints_rows(self, job_id: int) -> List[Dict[str, Any]]: + return self._select("endpoints", "jobId = ?", (int(job_id),)) + + def fetch_schedule_row(self, job_id: int) -> Optional[Dict[str, Any]]: + return self._one("schedule", "jobId = ?", (int(job_id),)) + + # ------------------------------- + # Writes + # ------------------------------- + + def upsert(self, job: Job) -> int: + """Insert or update a job + its endpoints + schedule. Returns job id.""" + + row_dicts = job.to_row_dicts() + job_row: Dict[str, Any] = row_dicts["job_row"] + endpoint_rows: List[Dict[str, Any]] = row_dicts["endpoint_rows"] + schedule_row: Dict[str, Any] = row_dicts["schedule_row"] + + with self._transaction(): + # --- jobs --- + data = { + "name": job_row.get("name"), + "enabled": int(job_row.get("enabled") or 0), + "mode": job_row.get("mode") or "mirror", + "direction": job_row.get("direction") or "unidirectional", + "allowDeletion": int(job_row.get("allowDeletion") or 0), + "preserveMetadata": int(job_row.get("preserveMetadata") or 0), + "pairId": job_row.get("pairId"), + "conflictPolicy": job_row.get("conflictPolicy") or "newest", + "lastRun": job_row.get("lastRun"), + "lastResult": job_row.get("lastResult"), + "lastError": job_row.get("lastError"), + } + + if job.id: + self._update("jobs", data, "id = :id", {"id": int(job.id)}) + job_id = int(job.id) + else: + job_id = self._insert("jobs", data) + job.id = job_id + + # --- endpoints --- + for ep in endpoint_rows: + role = ep.get("role") + if role not in ("source", "target"): + continue + ep_data = { + "jobId": job_id, + "role": role, + "type": ep.get("type"), + "location": ep.get("location"), + "port": ep.get("port"), + "guest": ep.get("guest"), + "username": ep.get("username"), + "password": ep.get("password"), + "useKey": ep.get("useKey"), + "sshKey": ep.get("sshKey"), + "options": ep.get("options"), + } + # Unique index on (jobId, role) + self._upsert("endpoints", ep_data, ["jobId", "role"], update_columns=[ + "type", "location", "port", "guest", "username", "password", "useKey", "sshKey", "options" + ]) + + # --- schedule --- + s_data = { + "jobId": job_id, + "enabled": int(schedule_row.get("enabled") or 0), + "everyMinutes": int(schedule_row.get("everyMinutes") or 60), + "nextRunAt": schedule_row.get("nextRunAt"), + "lastScheduledRunAt": schedule_row.get("lastScheduledRunAt"), + } + # schedule.jobId is UNIQUE + self._upsert("schedule", s_data, ["jobId"], update_columns=[ + "enabled", "everyMinutes", "nextRunAt", "lastScheduledRunAt" + ]) + + return int(job.id or 0) + + def delete(self, job_id: int) -> None: + with self._transaction(): + self._delete("jobs", "id = ?", (int(job_id),)) diff --git a/src/replicator/migration.py b/src/replicator/migration.py new file mode 100644 index 0000000..8fec9b8 --- /dev/null +++ b/src/replicator/migration.py @@ -0,0 +1,260 @@ + +#!/usr/bin/env python3 +# src/replicator/migration.py + +from __future__ import annotations + +from typing import List, Optional, Sequence, Tuple + +try: + from core.database.sqlite import SQLite + from core.log import Log +except ImportError: + from database.sqlite import SQLite + from log import Log + + +class Migration: + """Database schema creation + migrations for Replicator. + + This class intentionally *does not* re-implement a DB wrapper. + It relies on corePY's `SQLite` for connections/transactions and only + owns Replicator's schema + migration history + small meta KV helpers. + """ + + def __init__(self, db: SQLite, logger: Optional[Log] = None): + self._db = db + self._logger = logger + + # ------------------------------------------------------------------ + # Public API + # ------------------------------------------------------------------ + + def ensure(self) -> None: + """Ensure base tables exist and apply all pending migrations.""" + self._ensure_schema_migrations_table() + self._apply_migrations(self._migrations()) + + def get_meta(self, key: str, default: Optional[str] = None) -> Optional[str]: + """Read a value from the `meta` table. Returns default if missing.""" + try: + row = self._db.one("SELECT value FROM meta WHERE key = ?", (key,)) + if not row: + return default + val = row.get("value") + return default if val is None else str(val) + except Exception: + return default + + def set_meta(self, key: str, value: Optional[str]) -> None: + """Upsert a value into the `meta` table.""" + # meta table is created by migration 0003, but this is called later + # (maintenance). If a user runs an older DB, this will no-op safely. + try: + with self._db.transaction(): + exists = self._db.scalar("SELECT 1 FROM meta WHERE key = ? LIMIT 1", (key,)) + if exists: + self._db.execute( + "UPDATE meta SET value = ?, modified = CURRENT_TIMESTAMP WHERE key = ?", + (value, key), + ) + else: + self._db.execute( + "INSERT INTO meta (key, value) VALUES (?, ?)", + (key, value), + ) + except Exception: + # best effort + pass + + # ------------------------------------------------------------------ + # Internals + # ------------------------------------------------------------------ + + def _log(self, msg: str, *, level: str = "info", channel: str = "migration") -> None: + if self._logger is not None and hasattr(self._logger, "append"): + self._logger.append(msg, level=level, channel=channel) # type: ignore[call-arg] + else: + print(msg) + + def _ensure_schema_migrations_table(self) -> None: + self._db.execute( + """ + CREATE TABLE IF NOT EXISTS schema_migrations ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + created DATETIME DEFAULT CURRENT_TIMESTAMP, + modified DATETIME DEFAULT CURRENT_TIMESTAMP, + name TEXT NOT NULL UNIQUE + ); + """ + ) + + def _is_applied(self, name: str) -> bool: + r = self._db.scalar( + "SELECT 1 FROM schema_migrations WHERE name = ? LIMIT 1;", + (name,), + ) + return bool(r) + + def _apply_migrations(self, migrations: Sequence[Tuple[str, Sequence[str]]]) -> None: + for name, stmts in migrations: + if self._is_applied(name): + continue + + try: + with self._db.transaction(): + for stmt in stmts: + self._db.execute(stmt) + self._db.execute("INSERT INTO schema_migrations (name) VALUES (?)", (name,)) + + self._log(f"[Migration] applied {name}", level="debug") + except Exception as e: + raise RuntimeError(f"Failed to apply migration {name}: {e}") + + def _migrations(self) -> List[Tuple[str, List[str]]]: + return [ + ( + "0001_init", + [ + # jobs + """ + CREATE TABLE IF NOT EXISTS jobs ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + created DATETIME DEFAULT CURRENT_TIMESTAMP, + modified DATETIME DEFAULT CURRENT_TIMESTAMP, + name TEXT NOT NULL, + enabled INTEGER NOT NULL DEFAULT 1, + mode TEXT NOT NULL DEFAULT 'mirror', + direction TEXT NOT NULL DEFAULT 'unidirectional', + allowDeletion INTEGER NOT NULL DEFAULT 0, + preserveMetadata INTEGER NOT NULL DEFAULT 1, + pairId TEXT NULL, + conflictPolicy TEXT NOT NULL DEFAULT 'newest', + lastRun TEXT NULL, + lastResult TEXT NULL, + lastError TEXT NULL + ); + """, + "CREATE INDEX IF NOT EXISTS idx_jobs_enabled ON jobs(enabled);", + + # endpoints + """ + CREATE TABLE IF NOT EXISTS endpoints ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + created DATETIME DEFAULT CURRENT_TIMESTAMP, + modified DATETIME DEFAULT CURRENT_TIMESTAMP, + jobId INTEGER NOT NULL, + role TEXT NOT NULL, + type TEXT NOT NULL, + location TEXT NOT NULL, + port INTEGER NULL, + guest INTEGER NOT NULL DEFAULT 1, + username TEXT NULL, + password TEXT NULL, + useKey INTEGER NOT NULL DEFAULT 0, + sshKey TEXT NULL, + options TEXT NULL, + FOREIGN KEY(jobId) REFERENCES jobs(id) ON DELETE CASCADE + ); + """, + "CREATE UNIQUE INDEX IF NOT EXISTS uq_endpoints_job_role ON endpoints(jobId, role);", + + # schedule + """ + CREATE TABLE IF NOT EXISTS schedule ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + created DATETIME DEFAULT CURRENT_TIMESTAMP, + modified DATETIME DEFAULT CURRENT_TIMESTAMP, + jobId INTEGER NOT NULL UNIQUE, + enabled INTEGER NOT NULL DEFAULT 0, + everyMinutes INTEGER NOT NULL DEFAULT 60, + nextRunAt TEXT NULL, + lastScheduledRunAt TEXT NULL, + FOREIGN KEY(jobId) REFERENCES jobs(id) ON DELETE CASCADE + ); + """, + + # runs + """ + CREATE TABLE IF NOT EXISTS runs ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + created DATETIME DEFAULT CURRENT_TIMESTAMP, + modified DATETIME DEFAULT CURRENT_TIMESTAMP, + jobId INTEGER NOT NULL, + startedAt TEXT NOT NULL, + endedAt TEXT NULL, + result TEXT NOT NULL, + message TEXT NULL, + stats TEXT NULL, + FOREIGN KEY(jobId) REFERENCES jobs(id) ON DELETE CASCADE + ); + """, + "CREATE INDEX IF NOT EXISTS idx_runs_job_started ON runs(jobId, startedAt);", + + # file_state + """ + CREATE TABLE IF NOT EXISTS file_state ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + created DATETIME DEFAULT CURRENT_TIMESTAMP, + modified DATETIME DEFAULT CURRENT_TIMESTAMP, + jobId INTEGER NOT NULL, + side TEXT NOT NULL, + relPath TEXT NOT NULL, + size INTEGER NOT NULL DEFAULT 0, + mtime INTEGER NOT NULL DEFAULT 0, + hash TEXT NULL, + isDir INTEGER NOT NULL DEFAULT 0, + deleted INTEGER NOT NULL DEFAULT 0, + deletedAt TEXT NULL, + meta TEXT NULL, + FOREIGN KEY(jobId) REFERENCES jobs(id) ON DELETE CASCADE + ); + """, + "CREATE UNIQUE INDEX IF NOT EXISTS uq_file_state ON file_state(jobId, side, relPath);", + + # conflicts + """ + CREATE TABLE IF NOT EXISTS conflicts ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + created DATETIME DEFAULT CURRENT_TIMESTAMP, + modified DATETIME DEFAULT CURRENT_TIMESTAMP, + jobId INTEGER NOT NULL, + runId INTEGER NULL, + relPath TEXT NOT NULL, + a_size INTEGER NULL, + a_mtime INTEGER NULL, + a_hash TEXT NULL, + b_size INTEGER NULL, + b_mtime INTEGER NULL, + b_hash TEXT NULL, + status TEXT NOT NULL DEFAULT 'open', + resolution TEXT NULL, + note TEXT NULL, + FOREIGN KEY(jobId) REFERENCES jobs(id) ON DELETE CASCADE, + FOREIGN KEY(runId) REFERENCES runs(id) ON DELETE SET NULL + ); + """, + "CREATE INDEX IF NOT EXISTS idx_conflicts_job_status ON conflicts(jobId, status);", + ], + ), + ( + "0002_file_state_last_seen", + [ + "ALTER TABLE file_state ADD COLUMN lastSeenAt TEXT NULL;", + "ALTER TABLE file_state ADD COLUMN lastSeenRunId INTEGER NULL;", + ], + ), + ( + "0003_meta_kv", + [ + """ + CREATE TABLE IF NOT EXISTS meta ( + key TEXT PRIMARY KEY, + created DATETIME DEFAULT CURRENT_TIMESTAMP, + modified DATETIME DEFAULT CURRENT_TIMESTAMP, + value TEXT NULL + ); + """, + ], + ), + ] diff --git a/src/replicator/replicator.py b/src/replicator/replicator.py index 249999b..993ea16 100644 --- a/src/replicator/replicator.py +++ b/src/replicator/replicator.py @@ -9,13 +9,12 @@ import sqlite3 import json import shutil -import hashlib # Add datetime import for lastRun/lastResult from datetime import datetime, timezone from PyQt5.QtCore import Qt -from PyQt5.QtGui import QIcon, QPixmap +from PyQt5.QtGui import QPixmap from PyQt5.QtWidgets import ( QApplication, QMainWindow, @@ -23,36 +22,28 @@ QVBoxLayout, QHBoxLayout, QLabel, - QPushButton, QTableWidget, QTableWidgetItem, QHeaderView, QDialog, - QFormLayout, - QLineEdit, - QCheckBox, - QComboBox, - QSpinBox, - QStackedWidget, - QTextEdit, - QFrame, - QSizePolicy, - QLayout, ) +from .ui import JobDialog, ScheduleDialog + try: from core.helper import Helper from core.configuration import Configuration from core.log import Log from core.ui import MsgBox, Form from core.filesystem.filesystem import FileSystem + from core.database.sqlite import SQLite except ImportError: from helper import Helper from configuration import Configuration from log import Log from ui import MsgBox, Form from filesystem.filesystem import FileSystem - + from database.sqlite import SQLite # ------------------------------------------------------------------ # ReplicatorDB: SQLite storage/migrations @@ -299,709 +290,6 @@ def _ensure_modified_trigger(self, table_name: str): """) - -class JobDialog(QDialog): - def __init__(self, parent=None, job: Optional[Dict[str, Any]] = None): - super().__init__(parent) - self.setWindowTitle("Replication Job") - self.setModal(True) - self.setFixedHeight(400) - self.setMinimumHeight(400) - self.setMinimumWidth(1200) - - job = job or {} - self._original_job = job - - layout = QVBoxLayout(self) - layout.setAlignment(Qt.AlignTop) - layout.setSizeConstraint(QLayout.SetMinimumSize) - - # ------------------------------ - # Name row (full width) - # ------------------------------ - name_row = QHBoxLayout() - name_row.addWidget(QLabel("Name")) - self.name = QLineEdit(job.get("name", "")) - name_row.addWidget(self.name, 1) - layout.addLayout(name_row) - - # Helper to get endpoint dict from job or fallback - def _get_endpoint(key_endpoint: str, key_str: str, default_type: str = "local") -> Dict[str, Any]: - ep = job.get(key_endpoint) - if isinstance(ep, dict): - return dict(ep) - return { - "type": default_type, - "location": job.get(key_str, ""), - "auth": {}, - } - - self._source_ep = _get_endpoint("sourceEndpoint", "source", "local") - self._target_ep = _get_endpoint("targetEndpoint", "target", "local") - - # ------------------------------ - # Two-column area: Source / Destination - # ------------------------------ - cols = QHBoxLayout() - cols.setSpacing(20) - # Keep endpoint panels compact (avoid vertical stretching) - cols.setAlignment(Qt.AlignTop) - - # Bordered containers for better visual separation - src_frame = QFrame() - src_frame.setObjectName("EndpointFrame") - src_frame.setFrameShape(QFrame.NoFrame) - src_frame.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Maximum) - src_frame.setMinimumHeight(0) - src_frame.setStyleSheet( - "#EndpointFrame { border: 1px solid rgba(255,255,255,0.12); border-radius: 8px; padding: 10px; }" - ) - src_col = QVBoxLayout(src_frame) - src_col.setContentsMargins(10, 10, 10, 10) - src_col.setSpacing(8) - src_col.setAlignment(Qt.AlignTop) - - dst_frame = QFrame() - dst_frame.setObjectName("EndpointFrame") - dst_frame.setFrameShape(QFrame.NoFrame) - dst_frame.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Maximum) - dst_frame.setMinimumHeight(0) - dst_frame.setStyleSheet( - "#EndpointFrame { border: 1px solid rgba(255,255,255,0.12); border-radius: 8px; padding: 10px; }" - ) - dst_col = QVBoxLayout(dst_frame) - dst_col.setContentsMargins(10, 10, 10, 10) - dst_col.setSpacing(8) - dst_col.setAlignment(Qt.AlignTop) - - src_title = QLabel("Source") - src_title.setStyleSheet("font-weight: 600;") - dst_title = QLabel("Destination") - dst_title.setStyleSheet("font-weight: 600;") - - src_col.addWidget(src_title) - dst_col.addWidget(dst_title) - - ( - self._source_type_combo, - self._source_location_edit, - self._source_port_spin, - self._source_endpoint_row, - self._source_auth_widget, - self._source_auth_widgets, - ) = self._build_endpoint(existing=self._source_ep) - - ( - self._target_type_combo, - self._target_location_edit, - self._target_port_spin, - self._target_endpoint_row, - self._target_auth_widget, - self._target_auth_widgets, - ) = self._build_endpoint(existing=self._target_ep) - - # Endpoint row: Type + Location (+ Port for FTP/SSH) - src_col.addWidget(self._source_endpoint_row) - src_col.addWidget(self._source_auth_widget) - - dst_col.addWidget(self._target_endpoint_row) - dst_col.addWidget(self._target_auth_widget) - - cols.addWidget(src_frame, 1) - cols.addWidget(dst_frame, 1) - layout.addLayout(cols) - - # ------------------------------ - # Mode + Direction on same line - # ------------------------------ - self.mode = QComboBox() - self.mode.addItems(["mirror"]) - mode_val = job.get("mode", "mirror") - idx = self.mode.findText(mode_val) - if idx >= 0: - self.mode.setCurrentIndex(idx) - - self.direction = QComboBox() - self.direction.addItems(["unidirectional", "bidirectional"]) - direction_val = job.get("direction", "unidirectional") - idx = self.direction.findText(direction_val) - if idx >= 0: - self.direction.setCurrentIndex(idx) - - # Mode + Direction on same line, each taking half width - mode_dir_row = QHBoxLayout() - - mode_wrap = QWidget() - mode_wrap.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) - mode_lay = QHBoxLayout(mode_wrap) - mode_lay.setContentsMargins(0, 0, 0, 0) - mode_lay.setSpacing(8) - mode_lay.addWidget(QLabel("Mode")) - mode_lay.addWidget(self.mode, 1) - - dir_wrap = QWidget() - dir_wrap.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) - dir_lay = QHBoxLayout(dir_wrap) - dir_lay.setContentsMargins(0, 0, 0, 0) - dir_lay.setSpacing(8) - dir_lay.addWidget(QLabel("Direction")) - dir_lay.addWidget(self.direction, 1) - - self.mode.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) - self.direction.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) - - mode_dir_row.addWidget(mode_wrap, 1) - mode_dir_row.addWidget(dir_wrap, 1) - layout.addLayout(mode_dir_row) - - # ------------------------------ - # Other options (kept simple) - # ------------------------------ - opts_row = QHBoxLayout() - self.allow_deletion = QCheckBox("Allow deletion") - self.allow_deletion.setChecked(bool(job.get("allowDeletion", False))) - self.preserve_metadata = QCheckBox("Preserve metadata") - self.preserve_metadata.setChecked(bool(job.get("preserveMetadata", True))) - self.enabled = QCheckBox("Enabled") - self.enabled.setChecked(bool(job.get("enabled", True))) - opts_row.addWidget(self.allow_deletion) - opts_row.addSpacing(16) - opts_row.addWidget(self.preserve_metadata) - opts_row.addWidget(self.enabled) - opts_row.addStretch(1) - layout.addLayout(opts_row) - - # ------------------------------ - # Buttons - # ------------------------------ - btn_row = QHBoxLayout() - btn_row.addStretch(1) - cancel_btn = QPushButton("Cancel") - ok_btn = QPushButton("Save") - cancel_btn.clicked.connect(self.reject) - ok_btn.clicked.connect(self._on_ok) - btn_row.addWidget(cancel_btn) - btn_row.addWidget(ok_btn) - layout.addLayout(btn_row) - - # Wire type changes - self._source_type_combo.currentIndexChanged.connect(lambda _i: self._on_type_changed( - self._source_type_combo, - self._source_location_edit, - self._source_port_spin, - self._source_auth_widget, - self._source_auth_widgets, - )) - self._target_type_combo.currentIndexChanged.connect(lambda _i: self._on_type_changed( - self._target_type_combo, - self._target_location_edit, - self._target_port_spin, - self._target_auth_widget, - self._target_auth_widgets, - )) - - # Trigger initial state - self._on_type_changed( - self._source_type_combo, - self._source_location_edit, - self._source_port_spin, - self._source_auth_widget, - self._source_auth_widgets, - initial=True, - ) - self._on_type_changed( - self._target_type_combo, - self._target_location_edit, - self._target_port_spin, - self._target_auth_widget, - self._target_auth_widgets, - initial=True, - ) - - # ------------------------------------------------------------------ - # Endpoint UI - # ------------------------------------------------------------------ - - def _build_endpoint(self, existing: Dict[str, Any]): - """Build endpoint widgets. - - Returns: - (type_combo, location_edit, port_spin, endpoint_row_widget, auth_widget, widgets_dict) - """ - # Top row: [Type] [Location] [Port (FTP/SSH)] - type_combo = QComboBox() - type_combo.addItems(["local", "smb", "ftp", "ssh"]) - idx = type_combo.findText(existing.get("type", "local")) - if idx >= 0: - type_combo.setCurrentIndex(idx) - type_combo.setFixedWidth(110) - # Allow _on_type_changed to access original config - type_combo.setProperty("existing_type", existing.get("type", "local")) - type_combo.setProperty("existing_auth", existing.get("auth", {}) or {}) - - location_edit = QLineEdit(existing.get("location", "")) - - port_spin = QSpinBox() - port_spin.setRange(1, 65535) - port_spin.setFixedWidth(110) - # Default ports (used when the type is FTP/SSH) - existing_type = existing.get("type", "local") - existing_auth = existing.get("auth", {}) or {} - if existing_type == "ftp": - port_spin.setValue(int(existing_auth.get("port", 21) or 21)) - elif existing_type == "ssh": - port_spin.setValue(int(existing_auth.get("port", 22) or 22)) - else: - # Keep a sane default value even when hidden - port_spin.setValue(21) - - # Endpoint row widget with label: [Label] [Type] [Location] [Port] - endpoint_fields = QWidget() - fields_lay = QHBoxLayout(endpoint_fields) - fields_lay.setContentsMargins(0, 0, 0, 0) - fields_lay.setSpacing(8) - fields_lay.addWidget(type_combo) - fields_lay.addWidget(location_edit, 1) - fields_lay.addWidget(port_spin) - - endpoint_row = QWidget() - endpoint_row.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) - endpoint_form = QFormLayout(endpoint_row) - endpoint_form.setContentsMargins(0, 0, 0, 0) - endpoint_form.setSpacing(6) - endpoint_form.addRow(endpoint_fields) - - # Auth widget (shown only when non-local) - # Use a dedicated sub-container that shrinks/grows with its visible children. - auth_widget = QFrame() - auth_widget.setFrameShape(QFrame.NoFrame) - auth_widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Maximum) - auth_widget.setMinimumHeight(0) - - auth_lay = QVBoxLayout(auth_widget) - auth_lay.setContentsMargins(0, 0, 0, 0) - auth_lay.setSpacing(6) - auth_lay.setAlignment(Qt.AlignTop) - - auth_title = QLabel("Authentication") - auth_title.setStyleSheet("font-weight: 600;") - - widgets: Dict[str, Any] = {} - - # SMB auth - smb_wrap = QWidget() - smb_wrap.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Maximum) - smb_form = QFormLayout(smb_wrap) - smb_form.setContentsMargins(0, 0, 0, 0) - smb_guest = QCheckBox("Login as Guest") - smb_user_lbl = QLabel("Username") - smb_username = QLineEdit() - smb_pass_lbl = QLabel("Password") - smb_password = QLineEdit() - smb_password.setEchoMode(QLineEdit.Password) - - smb_auth = existing.get("auth", {}) if existing.get("type") == "smb" else {} - smb_guest.setChecked(bool(smb_auth.get("guest", True))) # default guest ON - smb_username.setText(smb_auth.get("username", "")) - smb_password.setText(smb_auth.get("password", "")) - - smb_form.addRow(smb_guest) - smb_form.addRow(smb_user_lbl, smb_username) - smb_form.addRow(smb_pass_lbl, smb_password) - - def _smb_guest_update(): - guest = smb_guest.isChecked() - smb_user_lbl.setVisible(not guest) - smb_username.setVisible(not guest) - smb_pass_lbl.setVisible(not guest) - smb_password.setVisible(not guest) - - smb_guest.stateChanged.connect(_smb_guest_update) - _smb_guest_update() - - # FTP auth - ftp_wrap = QWidget() - ftp_wrap.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Maximum) - ftp_form = QFormLayout(ftp_wrap) - ftp_form.setContentsMargins(0, 0, 0, 0) - ftp_guest = QCheckBox("Login as Guest") - ftp_user_lbl = QLabel("Username") - ftp_username = QLineEdit() - ftp_pass_lbl = QLabel("Password") - ftp_password = QLineEdit() - ftp_password.setEchoMode(QLineEdit.Password) - - ftp_auth = existing.get("auth", {}) if existing.get("type") == "ftp" else {} - ftp_guest.setChecked(bool(ftp_auth.get("guest", True))) # default guest ON - ftp_username.setText(ftp_auth.get("username", "")) - ftp_password.setText(ftp_auth.get("password", "")) - - ftp_form.addRow(ftp_guest) - ftp_form.addRow(ftp_user_lbl, ftp_username) - ftp_form.addRow(ftp_pass_lbl, ftp_password) - - def _ftp_guest_update(): - guest = ftp_guest.isChecked() - ftp_user_lbl.setVisible(not guest) - ftp_username.setVisible(not guest) - ftp_pass_lbl.setVisible(not guest) - ftp_password.setVisible(not guest) - - ftp_guest.stateChanged.connect(_ftp_guest_update) - _ftp_guest_update() - - # SSH auth - ssh_wrap = QWidget() - ssh_wrap.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Maximum) - ssh_form = QFormLayout(ssh_wrap) - ssh_form.setContentsMargins(0, 0, 0, 0) - ssh_use_key = QCheckBox("Use SSH Key") - ssh_user_lbl = QLabel("Username") - ssh_username = QLineEdit() - ssh_pass_lbl = QLabel("Password") - ssh_password = QLineEdit() - ssh_password.setEchoMode(QLineEdit.Password) - ssh_key_lbl = QLabel("SSH Key") - ssh_key_text = QTextEdit() - ssh_key_text.setPlaceholderText("Paste SSH private key here...") - ssh_key_text.setFixedHeight(110) - - ssh_auth = existing.get("auth", {}) if existing.get("type") == "ssh" else {} - ssh_username.setText(ssh_auth.get("username", "")) - ssh_password.setText(ssh_auth.get("password", "")) - ssh_key_text.setPlainText(ssh_auth.get("key", "")) - # Default: use key if no password provided - ssh_use_key.setChecked(bool(ssh_auth.get("useKey", True if not ssh_password.text().strip() else False))) - - ssh_form.addRow(ssh_use_key) - ssh_form.addRow(ssh_user_lbl, ssh_username) - ssh_form.addRow(ssh_pass_lbl, ssh_password) - ssh_form.addRow(ssh_key_lbl, ssh_key_text) - - def _ssh_use_key_update(): - use_key = ssh_use_key.isChecked() - # Username always visible - ssh_user_lbl.setVisible(True) - ssh_username.setVisible(True) - # Toggle password vs key - ssh_pass_lbl.setVisible(not use_key) - ssh_password.setVisible(not use_key) - ssh_key_lbl.setVisible(use_key) - ssh_key_text.setVisible(use_key) - - ssh_use_key.stateChanged.connect(_ssh_use_key_update) - _ssh_use_key_update() - - # Stacked display controlled by _on_type_changed - # We keep all wraps created and toggle visibility. - auth_lay.addWidget(auth_title) - auth_lay.addWidget(smb_wrap) - auth_lay.addWidget(ftp_wrap) - auth_lay.addWidget(ssh_wrap) - - widgets["smb"] = { - "wrap": smb_wrap, - "guest": smb_guest, - "user_lbl": smb_user_lbl, - "username": smb_username, - "pass_lbl": smb_pass_lbl, - "password": smb_password, - } - widgets["ftp"] = { - "wrap": ftp_wrap, - "guest": ftp_guest, - "user_lbl": ftp_user_lbl, - "username": ftp_username, - "pass_lbl": ftp_pass_lbl, - "password": ftp_password, - } - widgets["ssh"] = { - "wrap": ssh_wrap, - "useKey": ssh_use_key, - "user_lbl": ssh_user_lbl, - "username": ssh_username, - "pass_lbl": ssh_pass_lbl, - "password": ssh_password, - "key_lbl": ssh_key_lbl, - "key": ssh_key_text, - } - - return type_combo, location_edit, port_spin, endpoint_row, auth_widget, widgets - - def _on_type_changed( - self, - type_combo: QComboBox, - location_edit: QLineEdit, - port_spin: QSpinBox, - auth_widget: QWidget, - widgets: Dict[str, Any], - initial: bool = False, - ): - typ = type_combo.currentText() - - # Placeholders - placeholder = { - "local": "Local path (e.g. /data or C:\\Data)", - "smb": "SMB path (e.g. \\\\SERVER\\Share\\Folder)", - "ftp": "FTP host/path (e.g. ftp.example.com:/folder)", - "ssh": "SSH host/path (e.g. example.com:/folder)", - }.get(typ, "") - location_edit.setPlaceholderText(placeholder) - - # Port visibility + defaults - if typ in ("ftp", "ssh"): - port_spin.setVisible(True) - - existing_type = type_combo.property("existing_type") or "local" - existing_auth = type_combo.property("existing_auth") or {} - - # If initial load and the saved endpoint type matches, prefer saved port - if initial and existing_type == typ: - if typ == "ftp": - port_spin.setValue(int(existing_auth.get("port", 21) or 21)) - else: # ssh - port_spin.setValue(int(existing_auth.get("port", 22) or 22)) - else: - # When switching types, fix common wrong/default values - cur = int(port_spin.value()) - if typ == "ftp" and cur in (0, 22): - port_spin.setValue(21) - elif typ == "ssh" and cur in (0, 21): - port_spin.setValue(22) - else: - port_spin.setVisible(False) - - # Auth visibility: collapse container when hidden, resize to content when shown - if typ == "local": - auth_widget.setVisible(False) - auth_widget.setMaximumHeight(0) - else: - auth_widget.setVisible(True) - auth_widget.setMaximumHeight(16777215) - - # Toggle which auth panel is visible - for key in ("smb", "ftp", "ssh"): - if key in widgets and "wrap" in widgets[key]: - widgets[key]["wrap"].setVisible(False) - - if typ in ("smb", "ftp", "ssh"): - widgets[typ]["wrap"].setVisible(True) - - # Defaults: guest ON for non-local SMB/FTP when not configured - if typ in ("smb", "ftp"): - w = widgets[typ] - if w["guest"].isChecked() is False: - # Only auto-enable guest if user/pass are empty - if not w["username"].text().strip() and not w["password"].text().strip(): - w["guest"].setChecked(True) - - # Force re-layout so the auth container shrinks/grows immediately - auth_widget.adjustSize() - if auth_widget.parentWidget() is not None: - auth_widget.parentWidget().adjustSize() - self.adjustSize() - - # ------------------------------------------------------------------ - # Validation - # ------------------------------------------------------------------ - - def _on_ok(self): - if not self.name.text().strip(): - MsgBox.show(self, "Job", "Name is required.", icon="warning") - return - - src_type = self._source_type_combo.currentText() - tgt_type = self._target_type_combo.currentText() - - src_loc = self._source_location_edit.text().strip() - tgt_loc = self._target_location_edit.text().strip() - - if not src_loc: - MsgBox.show(self, "Job", "Source location is required.", icon="warning") - return - if not tgt_loc: - MsgBox.show(self, "Job", "Target location is required.", icon="warning") - return - - # Source auth validation - if src_type in ("smb", "ftp"): - w = self._source_auth_widgets[src_type] - if not w["guest"].isChecked(): - if not w["username"].text().strip() or not w["password"].text().strip(): - MsgBox.show(self, "Job", "Source username and password are required.", icon="warning") - return - elif src_type == "ssh": - w = self._source_auth_widgets["ssh"] - if not w["username"].text().strip(): - MsgBox.show(self, "Job", "Source SSH username is required.", icon="warning") - return - use_key = w["useKey"].isChecked() - if use_key: - if not w["key"].toPlainText().strip(): - MsgBox.show(self, "Job", "Source SSH key is required when 'Use SSH Key' is enabled.", icon="warning") - return - else: - if not w["password"].text().strip(): - MsgBox.show(self, "Job", "Source SSH password is required when not using a key.", icon="warning") - return - - # Target auth validation - if tgt_type in ("smb", "ftp"): - w = self._target_auth_widgets[tgt_type] - if not w["guest"].isChecked(): - if not w["username"].text().strip() or not w["password"].text().strip(): - MsgBox.show(self, "Job", "Target username and password are required.", icon="warning") - return - elif tgt_type == "ssh": - w = self._target_auth_widgets["ssh"] - if not w["username"].text().strip(): - MsgBox.show(self, "Job", "Target SSH username is required.", icon="warning") - return - use_key = w["useKey"].isChecked() - if use_key: - if not w["key"].toPlainText().strip(): - MsgBox.show(self, "Job", "Target SSH key is required when 'Use SSH Key' is enabled.", icon="warning") - return - else: - if not w["password"].text().strip(): - MsgBox.show(self, "Job", "Target SSH password is required when not using a key.", icon="warning") - return - - self.accept() - - # ------------------------------------------------------------------ - # Value extraction - # ------------------------------------------------------------------ - - def value(self) -> Dict[str, Any]: - def _extract( - type_combo: QComboBox, - location_edit: QLineEdit, - port_spin: QSpinBox, - widgets: Dict[str, Any], - ) -> Dict[str, Any]: - typ = type_combo.currentText() - location = location_edit.text().strip() - auth: Dict[str, Any] = {} - - if typ == "local": - auth = {} - elif typ == "smb": - w = widgets["smb"] - auth = { - "guest": bool(w["guest"].isChecked()), - "username": w["username"].text().strip(), - "password": w["password"].text(), - } - elif typ == "ftp": - w = widgets["ftp"] - auth = { - "guest": bool(w["guest"].isChecked()), - "username": w["username"].text().strip(), - "password": w["password"].text(), - "port": int(port_spin.value()), - } - elif typ == "ssh": - w = widgets["ssh"] - auth = { - "useKey": bool(w["useKey"].isChecked()), - "username": w["username"].text().strip(), - "password": w["password"].text(), - "port": int(port_spin.value()), - "key": w["key"].toPlainText(), - } - - return { - "type": typ, - "location": location, - "auth": auth, - } - - source_ep = _extract( - self._source_type_combo, - self._source_location_edit, - self._source_port_spin, - self._source_auth_widgets, - ) - target_ep = _extract( - self._target_type_combo, - self._target_location_edit, - self._target_port_spin, - self._target_auth_widgets, - ) - - val: Dict[str, Any] = { - "name": self.name.text().strip(), - "sourceEndpoint": source_ep, - "targetEndpoint": target_ep, - # Backward compatibility - "source": source_ep["location"], - "target": target_ep["location"], - "allowDeletion": bool(self.allow_deletion.isChecked()), - "preserveMetadata": bool(self.preserve_metadata.isChecked()), - "mode": self.mode.currentText(), - "direction": self.direction.currentText(), - "enabled": bool(self.enabled.isChecked()), - } - - # Preserve schedule fields if they existed previously - if self._original_job and isinstance(self._original_job, dict): - sched = self._original_job.get("schedule") - if sched is not None: - val["schedule"] = sched - - return val - - -class ScheduleDialog(QDialog): - def __init__(self, parent=None, job: Optional[Dict[str, Any]] = None): - super().__init__(parent) - self.setWindowTitle("Schedule") - self.setModal(True) - - job = job or {} - schedule = job.get("schedule", {}) - - layout = QVBoxLayout(self) - layout.setAlignment(Qt.AlignTop) - layout.setSizeConstraint(QLayout.SetMinimumSize) - form = QFormLayout() - layout.addLayout(form) - - self.enabled = QCheckBox() - self.enabled.setChecked(bool(schedule.get("enabled", False))) - self.every_minutes = QSpinBox() - self.every_minutes.setRange(1, 10080) - self.every_minutes.setValue(int(schedule.get("everyMinutes", 60))) - - form.addRow("Enabled", self.enabled) - form.addRow("Every minutes", self.every_minutes) - - btn_row = QHBoxLayout() - btn_row.addStretch(1) - - cancel_btn = QPushButton("Cancel") - ok_btn = QPushButton("Save") - cancel_btn.clicked.connect(self.reject) - ok_btn.clicked.connect(self._on_ok) - - btn_row.addWidget(cancel_btn) - btn_row.addWidget(ok_btn) - layout.addLayout(btn_row) - - def _on_ok(self): - if self.enabled.isChecked() and self.every_minutes.value() < 1: - MsgBox.show(self, "Schedule", "Every minutes must be at least 1 if enabled.", icon="warning") - return - self.accept() - - def value(self) -> Dict[str, Any]: - return { - "enabled": bool(self.enabled.isChecked()), - "everyMinutes": int(self.every_minutes.value()), - } - - class Replicator(QMainWindow): # ------------------------------------------------------------------ diff --git a/src/replicator/ui.py b/src/replicator/ui.py new file mode 100644 index 0000000..880754b --- /dev/null +++ b/src/replicator/ui.py @@ -0,0 +1,737 @@ +#!/usr/bin/env python3 +# src/replicator/ui.py + +from __future__ import annotations + +from typing import Optional, Any, Dict + +from PyQt5.QtCore import Qt +from PyQt5.QtWidgets import ( + QWidget, + QVBoxLayout, + QHBoxLayout, + QLabel, + QPushButton, + QDialog, + QFormLayout, + QLineEdit, + QCheckBox, + QComboBox, + QSpinBox, + QTextEdit, + QFrame, + QSizePolicy, + QLayout, +) + +try: + from core.ui import MsgBox +except ImportError: + from ui import MsgBox + + +# ------------------------------------------------------------------ +# Job Dialog +# ------------------------------------------------------------------ +class JobDialog(QDialog): + def __init__(self, parent=None, job: Optional[Dict[str, Any]] = None): + super().__init__(parent) + self.setWindowTitle("Replication Job") + self.setModal(True) + self.setFixedHeight(400) + self.setMinimumHeight(400) + self.setMinimumWidth(1200) + + job = job or {} + self._original_job = job + + layout = QVBoxLayout(self) + layout.setAlignment(Qt.AlignTop) + layout.setSizeConstraint(QLayout.SetMinimumSize) + + # ------------------------------ + # Name row (full width) + # ------------------------------ + name_row = QHBoxLayout() + name_row.addWidget(QLabel("Name")) + self.name = QLineEdit(job.get("name", "")) + name_row.addWidget(self.name, 1) + layout.addLayout(name_row) + + # Helper to get endpoint dict from job or fallback + def _get_endpoint(key_endpoint: str, key_str: str, default_type: str = "local") -> Dict[str, Any]: + ep = job.get(key_endpoint) + if isinstance(ep, dict): + return dict(ep) + return { + "type": default_type, + "location": job.get(key_str, ""), + "auth": {}, + } + + self._source_ep = _get_endpoint("sourceEndpoint", "source", "local") + self._target_ep = _get_endpoint("targetEndpoint", "target", "local") + + # ------------------------------ + # Two-column area: Source / Destination + # ------------------------------ + cols = QHBoxLayout() + cols.setSpacing(20) + # Keep endpoint panels compact (avoid vertical stretching) + cols.setAlignment(Qt.AlignTop) + + # Bordered containers for better visual separation + src_frame = QFrame() + src_frame.setObjectName("EndpointFrame") + src_frame.setFrameShape(QFrame.NoFrame) + src_frame.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Maximum) + src_frame.setMinimumHeight(0) + src_frame.setStyleSheet( + "#EndpointFrame { border: 1px solid rgba(255,255,255,0.12); border-radius: 8px; padding: 10px; }" + ) + src_col = QVBoxLayout(src_frame) + src_col.setContentsMargins(10, 10, 10, 10) + src_col.setSpacing(8) + src_col.setAlignment(Qt.AlignTop) + + dst_frame = QFrame() + dst_frame.setObjectName("EndpointFrame") + dst_frame.setFrameShape(QFrame.NoFrame) + dst_frame.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Maximum) + dst_frame.setMinimumHeight(0) + dst_frame.setStyleSheet( + "#EndpointFrame { border: 1px solid rgba(255,255,255,0.12); border-radius: 8px; padding: 10px; }" + ) + dst_col = QVBoxLayout(dst_frame) + dst_col.setContentsMargins(10, 10, 10, 10) + dst_col.setSpacing(8) + dst_col.setAlignment(Qt.AlignTop) + + src_title = QLabel("Source") + src_title.setStyleSheet("font-weight: 600;") + dst_title = QLabel("Destination") + dst_title.setStyleSheet("font-weight: 600;") + + src_col.addWidget(src_title) + dst_col.addWidget(dst_title) + + ( + self._source_type_combo, + self._source_location_edit, + self._source_port_spin, + self._source_endpoint_row, + self._source_auth_widget, + self._source_auth_widgets, + ) = self._build_endpoint(existing=self._source_ep) + + ( + self._target_type_combo, + self._target_location_edit, + self._target_port_spin, + self._target_endpoint_row, + self._target_auth_widget, + self._target_auth_widgets, + ) = self._build_endpoint(existing=self._target_ep) + + # Endpoint row: Type + Location (+ Port for FTP/SSH) + src_col.addWidget(self._source_endpoint_row) + src_col.addWidget(self._source_auth_widget) + + dst_col.addWidget(self._target_endpoint_row) + dst_col.addWidget(self._target_auth_widget) + + cols.addWidget(src_frame, 1) + cols.addWidget(dst_frame, 1) + layout.addLayout(cols) + + # ------------------------------ + # Mode + Direction on same line + # ------------------------------ + self.mode = QComboBox() + self.mode.addItems(["mirror"]) + mode_val = job.get("mode", "mirror") + idx = self.mode.findText(mode_val) + if idx >= 0: + self.mode.setCurrentIndex(idx) + + self.direction = QComboBox() + self.direction.addItems(["unidirectional", "bidirectional"]) + direction_val = job.get("direction", "unidirectional") + idx = self.direction.findText(direction_val) + if idx >= 0: + self.direction.setCurrentIndex(idx) + + # Mode + Direction on same line, each taking half width + mode_dir_row = QHBoxLayout() + + mode_wrap = QWidget() + mode_wrap.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) + mode_lay = QHBoxLayout(mode_wrap) + mode_lay.setContentsMargins(0, 0, 0, 0) + mode_lay.setSpacing(8) + mode_lay.addWidget(QLabel("Mode")) + mode_lay.addWidget(self.mode, 1) + + dir_wrap = QWidget() + dir_wrap.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) + dir_lay = QHBoxLayout(dir_wrap) + dir_lay.setContentsMargins(0, 0, 0, 0) + dir_lay.setSpacing(8) + dir_lay.addWidget(QLabel("Direction")) + dir_lay.addWidget(self.direction, 1) + + self.mode.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) + self.direction.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) + + mode_dir_row.addWidget(mode_wrap, 1) + mode_dir_row.addWidget(dir_wrap, 1) + layout.addLayout(mode_dir_row) + + # ------------------------------ + # Other options (kept simple) + # ------------------------------ + opts_row = QHBoxLayout() + self.allow_deletion = QCheckBox("Allow deletion") + self.allow_deletion.setChecked(bool(job.get("allowDeletion", False))) + self.preserve_metadata = QCheckBox("Preserve metadata") + self.preserve_metadata.setChecked(bool(job.get("preserveMetadata", True))) + self.enabled = QCheckBox("Enabled") + self.enabled.setChecked(bool(job.get("enabled", True))) + opts_row.addWidget(self.allow_deletion) + opts_row.addSpacing(16) + opts_row.addWidget(self.preserve_metadata) + opts_row.addWidget(self.enabled) + opts_row.addStretch(1) + layout.addLayout(opts_row) + + # ------------------------------ + # Buttons + # ------------------------------ + btn_row = QHBoxLayout() + btn_row.addStretch(1) + cancel_btn = QPushButton("Cancel") + ok_btn = QPushButton("Save") + cancel_btn.clicked.connect(self.reject) + ok_btn.clicked.connect(self._on_ok) + btn_row.addWidget(cancel_btn) + btn_row.addWidget(ok_btn) + layout.addLayout(btn_row) + + # Wire type changes + self._source_type_combo.currentIndexChanged.connect(lambda _i: self._on_type_changed( + self._source_type_combo, + self._source_location_edit, + self._source_port_spin, + self._source_auth_widget, + self._source_auth_widgets, + )) + self._target_type_combo.currentIndexChanged.connect(lambda _i: self._on_type_changed( + self._target_type_combo, + self._target_location_edit, + self._target_port_spin, + self._target_auth_widget, + self._target_auth_widgets, + )) + + # Trigger initial state + self._on_type_changed( + self._source_type_combo, + self._source_location_edit, + self._source_port_spin, + self._source_auth_widget, + self._source_auth_widgets, + initial=True, + ) + self._on_type_changed( + self._target_type_combo, + self._target_location_edit, + self._target_port_spin, + self._target_auth_widget, + self._target_auth_widgets, + initial=True, + ) + + # ------------------------------------------------------------------ + # Endpoint UI + # ------------------------------------------------------------------ + + def _build_endpoint(self, existing: Dict[str, Any]): + """Build endpoint widgets. + + Returns: + (type_combo, location_edit, port_spin, endpoint_row_widget, auth_widget, widgets_dict) + """ + # Top row: [Type] [Location] [Port (FTP/SSH)] + type_combo = QComboBox() + type_combo.addItems(["local", "smb", "ftp", "ssh"]) + idx = type_combo.findText(existing.get("type", "local")) + if idx >= 0: + type_combo.setCurrentIndex(idx) + type_combo.setFixedWidth(110) + # Allow _on_type_changed to access original config + type_combo.setProperty("existing_type", existing.get("type", "local")) + type_combo.setProperty("existing_auth", existing.get("auth", {}) or {}) + + location_edit = QLineEdit(existing.get("location", "")) + + port_spin = QSpinBox() + port_spin.setRange(1, 65535) + port_spin.setFixedWidth(110) + # Default ports (used when the type is FTP/SSH) + existing_type = existing.get("type", "local") + existing_auth = existing.get("auth", {}) or {} + if existing_type == "ftp": + port_spin.setValue(int(existing_auth.get("port", 21) or 21)) + elif existing_type == "ssh": + port_spin.setValue(int(existing_auth.get("port", 22) or 22)) + else: + # Keep a sane default value even when hidden + port_spin.setValue(21) + + # Endpoint row widget with label: [Label] [Type] [Location] [Port] + endpoint_fields = QWidget() + fields_lay = QHBoxLayout(endpoint_fields) + fields_lay.setContentsMargins(0, 0, 0, 0) + fields_lay.setSpacing(8) + fields_lay.addWidget(type_combo) + fields_lay.addWidget(location_edit, 1) + fields_lay.addWidget(port_spin) + + endpoint_row = QWidget() + endpoint_row.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) + endpoint_form = QFormLayout(endpoint_row) + endpoint_form.setContentsMargins(0, 0, 0, 0) + endpoint_form.setSpacing(6) + endpoint_form.addRow(endpoint_fields) + + # Auth widget (shown only when non-local) + # Use a dedicated sub-container that shrinks/grows with its visible children. + auth_widget = QFrame() + auth_widget.setFrameShape(QFrame.NoFrame) + auth_widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Maximum) + auth_widget.setMinimumHeight(0) + + auth_lay = QVBoxLayout(auth_widget) + auth_lay.setContentsMargins(0, 0, 0, 0) + auth_lay.setSpacing(6) + auth_lay.setAlignment(Qt.AlignTop) + + auth_title = QLabel("Authentication") + auth_title.setStyleSheet("font-weight: 600;") + + widgets: Dict[str, Any] = {} + + # SMB auth + smb_wrap = QWidget() + smb_wrap.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Maximum) + smb_form = QFormLayout(smb_wrap) + smb_form.setContentsMargins(0, 0, 0, 0) + smb_guest = QCheckBox("Login as Guest") + smb_user_lbl = QLabel("Username") + smb_username = QLineEdit() + smb_pass_lbl = QLabel("Password") + smb_password = QLineEdit() + smb_password.setEchoMode(QLineEdit.Password) + + smb_auth = existing.get("auth", {}) if existing.get("type") == "smb" else {} + smb_guest.setChecked(bool(smb_auth.get("guest", True))) # default guest ON + smb_username.setText(smb_auth.get("username", "")) + smb_password.setText(smb_auth.get("password", "")) + + smb_form.addRow(smb_guest) + smb_form.addRow(smb_user_lbl, smb_username) + smb_form.addRow(smb_pass_lbl, smb_password) + + def _smb_guest_update(): + guest = smb_guest.isChecked() + smb_user_lbl.setVisible(not guest) + smb_username.setVisible(not guest) + smb_pass_lbl.setVisible(not guest) + smb_password.setVisible(not guest) + + smb_guest.stateChanged.connect(_smb_guest_update) + _smb_guest_update() + + # FTP auth + ftp_wrap = QWidget() + ftp_wrap.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Maximum) + ftp_form = QFormLayout(ftp_wrap) + ftp_form.setContentsMargins(0, 0, 0, 0) + ftp_guest = QCheckBox("Login as Guest") + ftp_user_lbl = QLabel("Username") + ftp_username = QLineEdit() + ftp_pass_lbl = QLabel("Password") + ftp_password = QLineEdit() + ftp_password.setEchoMode(QLineEdit.Password) + + ftp_auth = existing.get("auth", {}) if existing.get("type") == "ftp" else {} + ftp_guest.setChecked(bool(ftp_auth.get("guest", True))) # default guest ON + ftp_username.setText(ftp_auth.get("username", "")) + ftp_password.setText(ftp_auth.get("password", "")) + + ftp_form.addRow(ftp_guest) + ftp_form.addRow(ftp_user_lbl, ftp_username) + ftp_form.addRow(ftp_pass_lbl, ftp_password) + + def _ftp_guest_update(): + guest = ftp_guest.isChecked() + ftp_user_lbl.setVisible(not guest) + ftp_username.setVisible(not guest) + ftp_pass_lbl.setVisible(not guest) + ftp_password.setVisible(not guest) + + ftp_guest.stateChanged.connect(_ftp_guest_update) + _ftp_guest_update() + + # SSH auth + ssh_wrap = QWidget() + ssh_wrap.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Maximum) + ssh_form = QFormLayout(ssh_wrap) + ssh_form.setContentsMargins(0, 0, 0, 0) + ssh_use_key = QCheckBox("Use SSH Key") + ssh_user_lbl = QLabel("Username") + ssh_username = QLineEdit() + ssh_pass_lbl = QLabel("Password") + ssh_password = QLineEdit() + ssh_password.setEchoMode(QLineEdit.Password) + ssh_key_lbl = QLabel("SSH Key") + ssh_key_text = QTextEdit() + ssh_key_text.setPlaceholderText("Paste SSH private key here...") + ssh_key_text.setFixedHeight(110) + + ssh_auth = existing.get("auth", {}) if existing.get("type") == "ssh" else {} + ssh_username.setText(ssh_auth.get("username", "")) + ssh_password.setText(ssh_auth.get("password", "")) + ssh_key_text.setPlainText(ssh_auth.get("key", "")) + # Default: use key if no password provided + ssh_use_key.setChecked(bool(ssh_auth.get("useKey", True if not ssh_password.text().strip() else False))) + + ssh_form.addRow(ssh_use_key) + ssh_form.addRow(ssh_user_lbl, ssh_username) + ssh_form.addRow(ssh_pass_lbl, ssh_password) + ssh_form.addRow(ssh_key_lbl, ssh_key_text) + + def _ssh_use_key_update(): + use_key = ssh_use_key.isChecked() + # Username always visible + ssh_user_lbl.setVisible(True) + ssh_username.setVisible(True) + # Toggle password vs key + ssh_pass_lbl.setVisible(not use_key) + ssh_password.setVisible(not use_key) + ssh_key_lbl.setVisible(use_key) + ssh_key_text.setVisible(use_key) + + ssh_use_key.stateChanged.connect(_ssh_use_key_update) + _ssh_use_key_update() + + # Stacked display controlled by _on_type_changed + # We keep all wraps created and toggle visibility. + auth_lay.addWidget(auth_title) + auth_lay.addWidget(smb_wrap) + auth_lay.addWidget(ftp_wrap) + auth_lay.addWidget(ssh_wrap) + + widgets["smb"] = { + "wrap": smb_wrap, + "guest": smb_guest, + "user_lbl": smb_user_lbl, + "username": smb_username, + "pass_lbl": smb_pass_lbl, + "password": smb_password, + } + widgets["ftp"] = { + "wrap": ftp_wrap, + "guest": ftp_guest, + "user_lbl": ftp_user_lbl, + "username": ftp_username, + "pass_lbl": ftp_pass_lbl, + "password": ftp_password, + } + widgets["ssh"] = { + "wrap": ssh_wrap, + "useKey": ssh_use_key, + "user_lbl": ssh_user_lbl, + "username": ssh_username, + "pass_lbl": ssh_pass_lbl, + "password": ssh_password, + "key_lbl": ssh_key_lbl, + "key": ssh_key_text, + } + + return type_combo, location_edit, port_spin, endpoint_row, auth_widget, widgets + + def _on_type_changed( + self, + type_combo: QComboBox, + location_edit: QLineEdit, + port_spin: QSpinBox, + auth_widget: QWidget, + widgets: Dict[str, Any], + initial: bool = False, + ): + typ = type_combo.currentText() + + # Placeholders + placeholder = { + "local": "Local path (e.g. /data or C:\\Data)", + "smb": "SMB path (e.g. \\\\SERVER\\Share\\Folder)", + "ftp": "FTP host/path (e.g. ftp.example.com:/folder)", + "ssh": "SSH host/path (e.g. example.com:/folder)", + }.get(typ, "") + location_edit.setPlaceholderText(placeholder) + + # Port visibility + defaults + if typ in ("ftp", "ssh"): + port_spin.setVisible(True) + + existing_type = type_combo.property("existing_type") or "local" + existing_auth = type_combo.property("existing_auth") or {} + + # If initial load and the saved endpoint type matches, prefer saved port + if initial and existing_type == typ: + if typ == "ftp": + port_spin.setValue(int(existing_auth.get("port", 21) or 21)) + else: # ssh + port_spin.setValue(int(existing_auth.get("port", 22) or 22)) + else: + # When switching types, fix common wrong/default values + cur = int(port_spin.value()) + if typ == "ftp" and cur in (0, 22): + port_spin.setValue(21) + elif typ == "ssh" and cur in (0, 21): + port_spin.setValue(22) + else: + port_spin.setVisible(False) + + # Auth visibility: collapse container when hidden, resize to content when shown + if typ == "local": + auth_widget.setVisible(False) + auth_widget.setMaximumHeight(0) + else: + auth_widget.setVisible(True) + auth_widget.setMaximumHeight(16777215) + + # Toggle which auth panel is visible + for key in ("smb", "ftp", "ssh"): + if key in widgets and "wrap" in widgets[key]: + widgets[key]["wrap"].setVisible(False) + + if typ in ("smb", "ftp", "ssh"): + widgets[typ]["wrap"].setVisible(True) + + # Defaults: guest ON for non-local SMB/FTP when not configured + if typ in ("smb", "ftp"): + w = widgets[typ] + if w["guest"].isChecked() is False: + # Only auto-enable guest if user/pass are empty + if not w["username"].text().strip() and not w["password"].text().strip(): + w["guest"].setChecked(True) + + # Force re-layout so the auth container shrinks/grows immediately + auth_widget.adjustSize() + if auth_widget.parentWidget() is not None: + auth_widget.parentWidget().adjustSize() + self.adjustSize() + + # ------------------------------------------------------------------ + # Validation + # ------------------------------------------------------------------ + + def _on_ok(self): + if not self.name.text().strip(): + MsgBox.show(self, "Job", "Name is required.", icon="warning") + return + + src_type = self._source_type_combo.currentText() + tgt_type = self._target_type_combo.currentText() + + src_loc = self._source_location_edit.text().strip() + tgt_loc = self._target_location_edit.text().strip() + + if not src_loc: + MsgBox.show(self, "Job", "Source location is required.", icon="warning") + return + if not tgt_loc: + MsgBox.show(self, "Job", "Target location is required.", icon="warning") + return + + # Source auth validation + if src_type in ("smb", "ftp"): + w = self._source_auth_widgets[src_type] + if not w["guest"].isChecked(): + if not w["username"].text().strip() or not w["password"].text().strip(): + MsgBox.show(self, "Job", "Source username and password are required.", icon="warning") + return + elif src_type == "ssh": + w = self._source_auth_widgets["ssh"] + if not w["username"].text().strip(): + MsgBox.show(self, "Job", "Source SSH username is required.", icon="warning") + return + use_key = w["useKey"].isChecked() + if use_key: + if not w["key"].toPlainText().strip(): + MsgBox.show(self, "Job", "Source SSH key is required when 'Use SSH Key' is enabled.", icon="warning") + return + else: + if not w["password"].text().strip(): + MsgBox.show(self, "Job", "Source SSH password is required when not using a key.", icon="warning") + return + + # Target auth validation + if tgt_type in ("smb", "ftp"): + w = self._target_auth_widgets[tgt_type] + if not w["guest"].isChecked(): + if not w["username"].text().strip() or not w["password"].text().strip(): + MsgBox.show(self, "Job", "Target username and password are required.", icon="warning") + return + elif tgt_type == "ssh": + w = self._target_auth_widgets["ssh"] + if not w["username"].text().strip(): + MsgBox.show(self, "Job", "Target SSH username is required.", icon="warning") + return + use_key = w["useKey"].isChecked() + if use_key: + if not w["key"].toPlainText().strip(): + MsgBox.show(self, "Job", "Target SSH key is required when 'Use SSH Key' is enabled.", icon="warning") + return + else: + if not w["password"].text().strip(): + MsgBox.show(self, "Job", "Target SSH password is required when not using a key.", icon="warning") + return + + self.accept() + + # ------------------------------------------------------------------ + # Value extraction + # ------------------------------------------------------------------ + + def value(self) -> Dict[str, Any]: + def _extract( + type_combo: QComboBox, + location_edit: QLineEdit, + port_spin: QSpinBox, + widgets: Dict[str, Any], + ) -> Dict[str, Any]: + typ = type_combo.currentText() + location = location_edit.text().strip() + auth: Dict[str, Any] = {} + + if typ == "local": + auth = {} + elif typ == "smb": + w = widgets["smb"] + auth = { + "guest": bool(w["guest"].isChecked()), + "username": w["username"].text().strip(), + "password": w["password"].text(), + } + elif typ == "ftp": + w = widgets["ftp"] + auth = { + "guest": bool(w["guest"].isChecked()), + "username": w["username"].text().strip(), + "password": w["password"].text(), + "port": int(port_spin.value()), + } + elif typ == "ssh": + w = widgets["ssh"] + auth = { + "useKey": bool(w["useKey"].isChecked()), + "username": w["username"].text().strip(), + "password": w["password"].text(), + "port": int(port_spin.value()), + "key": w["key"].toPlainText(), + } + + return { + "type": typ, + "location": location, + "auth": auth, + } + + source_ep = _extract( + self._source_type_combo, + self._source_location_edit, + self._source_port_spin, + self._source_auth_widgets, + ) + target_ep = _extract( + self._target_type_combo, + self._target_location_edit, + self._target_port_spin, + self._target_auth_widgets, + ) + + val: Dict[str, Any] = { + "name": self.name.text().strip(), + "sourceEndpoint": source_ep, + "targetEndpoint": target_ep, + # Backward compatibility + "source": source_ep["location"], + "target": target_ep["location"], + "allowDeletion": bool(self.allow_deletion.isChecked()), + "preserveMetadata": bool(self.preserve_metadata.isChecked()), + "mode": self.mode.currentText(), + "direction": self.direction.currentText(), + "enabled": bool(self.enabled.isChecked()), + } + + # Preserve schedule fields if they existed previously + if self._original_job and isinstance(self._original_job, dict): + sched = self._original_job.get("schedule") + if sched is not None: + val["schedule"] = sched + + return val + +# ------------------------------------------------------------------ +# Schedule Dialog +# ------------------------------------------------------------------ +class ScheduleDialog(QDialog): + def __init__(self, parent=None, job: Optional[Dict[str, Any]] = None): + super().__init__(parent) + self.setWindowTitle("Schedule") + self.setModal(True) + + job = job or {} + schedule = job.get("schedule", {}) + + layout = QVBoxLayout(self) + layout.setAlignment(Qt.AlignTop) + layout.setSizeConstraint(QLayout.SetMinimumSize) + form = QFormLayout() + layout.addLayout(form) + + self.enabled = QCheckBox() + self.enabled.setChecked(bool(schedule.get("enabled", False))) + self.every_minutes = QSpinBox() + self.every_minutes.setRange(1, 10080) + self.every_minutes.setValue(int(schedule.get("everyMinutes", 60))) + + form.addRow("Enabled", self.enabled) + form.addRow("Every minutes", self.every_minutes) + + btn_row = QHBoxLayout() + btn_row.addStretch(1) + + cancel_btn = QPushButton("Cancel") + ok_btn = QPushButton("Save") + cancel_btn.clicked.connect(self.reject) + ok_btn.clicked.connect(self._on_ok) + + btn_row.addWidget(cancel_btn) + btn_row.addWidget(ok_btn) + layout.addLayout(btn_row) + + def _on_ok(self): + if self.enabled.isChecked() and self.every_minutes.value() < 1: + MsgBox.show(self, "Schedule", "Every minutes must be at least 1 if enabled.", icon="warning") + return + self.accept() + + def value(self) -> Dict[str, Any]: + return { + "enabled": bool(self.enabled.isChecked()), + "everyMinutes": int(self.every_minutes.value()), + } From 05a521f2d3843df31f0fd446fda0f8e2b92f5110 Mon Sep 17 00:00:00 2001 From: Louis Ouellet Date: Thu, 15 Jan 2026 12:26:57 -0500 Subject: [PATCH 21/51] General: Updated corePY --- src/core | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core b/src/core index 120c5da..fa8b9f5 160000 --- a/src/core +++ b/src/core @@ -1 +1 @@ -Subproject commit 120c5da0e622c08b4cb6c273e9d97471701f22f5 +Subproject commit fa8b9f519c1b133601744a27504f6d262a94721f From 2b50f4e3de1f85b28be9fdebf815fcab6d2a4022 Mon Sep 17 00:00:00 2001 From: Louis Ouellet Date: Thu, 15 Jan 2026 12:38:44 -0500 Subject: [PATCH 22/51] General: Updated corePY --- src/core | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core b/src/core index fa8b9f5..9f00fb8 160000 --- a/src/core +++ b/src/core @@ -1 +1 @@ -Subproject commit fa8b9f519c1b133601744a27504f6d262a94721f +Subproject commit 9f00fb8dc4bc66e1fdcd4f568fb23f1c6711a3d0 From f39b459bcef0555721e8c6e67f8229f4454611a1 Mon Sep 17 00:00:00 2001 From: Louis Ouellet Date: Thu, 15 Jan 2026 12:44:46 -0500 Subject: [PATCH 23/51] General: Splitting replicator.py into more manageable chunks. --- src/replicator/__init__.py | 1 + src/replicator/job.py | 219 ++-------- src/replicator/migration.py | 11 +- src/replicator/replicator.py | 784 ++++++++--------------------------- src/replicator/ui.py | 295 +++++++------ 5 files changed, 396 insertions(+), 914 deletions(-) diff --git a/src/replicator/__init__.py b/src/replicator/__init__.py index b06ab96..7748ab7 100644 --- a/src/replicator/__init__.py +++ b/src/replicator/__init__.py @@ -3,6 +3,7 @@ from .replicator import Replicator from .ui import JobDialog, ScheduleDialog +from .job import Job, Schedule, Endpoint __version__ = "1.0.0" diff --git a/src/replicator/job.py b/src/replicator/job.py index 0790da8..840ffb8 100644 --- a/src/replicator/job.py +++ b/src/replicator/job.py @@ -5,9 +5,10 @@ from dataclasses import dataclass, field from datetime import datetime, timedelta, timezone, time -from typing import Any, Callable, Dict, Iterable, List, Mapping, Optional, Tuple, Union +from typing import Any, Callable, Dict, Iterable, List, Mapping, Optional, Tuple import json import sqlite3 + try: # corePY SQLite wrapper (preferred) from core.database.sqlite import SQLite # type: ignore @@ -24,14 +25,6 @@ @dataclass(frozen=True) class Endpoint: - """ - UI-agnostic endpoint description. - - Compatible with current DB schema in replicator.py: - - endpoints table: role, type, location, port, guest, username, password, useKey, sshKey, options - - Job stores endpoints as: - {"type": "...", "location": "...", "auth": {...}} - """ type: str = "local" location: str = "" auth: JsonDict = field(default_factory=dict) @@ -44,7 +37,6 @@ def validate(self, role: str) -> List[str]: errs.append(f"{role} endpoint location is required.") t = (self.type or "").lower() - # Basic sanity checks only (no network IO, no UI) if t in ("ftp", "ssh"): port = self.auth.get("port") if port is not None: @@ -57,12 +49,6 @@ def validate(self, role: str) -> List[str]: return errs def to_db_fields(self, role: str) -> JsonDict: - """ - Convert endpoint into endpoints-table fields. - Keeps compatibility with your current schema/logic: - - known auth keys are stored as columns - - extra auth keys are JSON in 'options' - """ t = (self.type or "local").lower() auth = dict(self.auth or {}) @@ -115,9 +101,6 @@ def to_db_fields(self, role: str) -> JsonDict: @staticmethod def from_db_row(row: Mapping[str, Any]) -> "Endpoint": - """ - Parse endpoints table row into Endpoint. - """ t = (row.get("type") or "local").lower() auth: JsonDict = {} @@ -154,29 +137,9 @@ def from_db_row(row: Mapping[str, Any]) -> "Endpoint": @dataclass(frozen=True) class Schedule: - """ - Job schedule config. - - Current DB has: - - enabled - - everyMinutes - - nextRunAt - - lastScheduledRunAt - - Upcoming feature: day/time windows. - - windows: dict weekday -> list of {start:"HH:MM", end:"HH:MM"} - weekday: 0=Mon ... 6=Sun (datetime.weekday()). - - Note: windows are not persisted yet by default; but we keep structure ready. - """ enabled: bool = False everyMinutes: int = 60 - - # Optional advanced scheduling windows: - # {0:[{"start":"22:00","end":"06:00"}], 6:[{"start":"00:00","end":"23:59"}], ...} - windows: JsonDict = field(default_factory=dict) - - # Optional DB fields (if you want to use them later) + windows: JsonDict = field(default_factory=dict) # persisted as JSON in schedule.windows nextRunAt: Optional[str] = None lastScheduledRunAt: Optional[str] = None @@ -185,7 +148,6 @@ def validate(self) -> List[str]: if self.enabled: if self.everyMinutes <= 0: errs.append("Schedule everyMinutes must be > 0 when schedule is enabled.") - # windows validation is tolerant (future UI can enforce strictness) if self.windows and not isinstance(self.windows, dict): errs.append("Schedule windows must be a dict of weekday -> list[window].") return errs @@ -204,11 +166,6 @@ def _parse_hhmm(self, s: str) -> Optional[time]: return None def _in_window_for_day(self, dt: datetime) -> bool: - """ - Returns True if dt is within any allowed window for dt.weekday(). - Supports overnight windows like 22:00-06:00. - If windows is empty => always allowed. - """ if not self.windows: return True @@ -232,7 +189,6 @@ def _in_window_for_day(self, dt: datetime) -> bool: continue if ts <= te: - # same-day window if ts <= tnow <= te: return True else: @@ -249,30 +205,14 @@ def should_run_now(self, now: Optional[datetime] = None) -> bool: return self._in_window_for_day(now) def next_run_at(self, now: Optional[datetime] = None) -> Optional[datetime]: - """ - Returns the next datetime the job is allowed to run based on: - - enabled - - everyMinutes - - windows (if any) - - Strategy: - - Start from next minute tick. - - Scan forward in 1-minute steps up to 7 days. - - Pick first time that is within an allowed window AND aligns to interval. - - This is deterministic and fast enough (<= 10080 iterations). - """ if not self.enabled: return None if self.everyMinutes <= 0: - # Interval 0 is "continuous" in your earlier convo; for scheduling we treat it as "run now" return now or datetime.now(timezone.utc) now = now or datetime.now(timezone.utc) - # Normalize to next minute (zero seconds/micros) cur = now.replace(second=0, microsecond=0) + timedelta(minutes=1) - # Align to interval relative to epoch minute 0 (UTC) def aligned(dt: datetime) -> bool: epoch = datetime(1970, 1, 1, tzinfo=timezone.utc) minutes = int((dt - epoch).total_seconds() // 60) @@ -292,28 +232,13 @@ class JobRunResult: ok: bool started_at: str ended_at: str - result: str # "ok"|"fail"|"running" + result: str message: Optional[str] = None stats: JsonDict = field(default_factory=dict) @dataclass class Job: - """ - UI-agnostic domain object. - - Owns: - - id, name, enabled, mode, direction, allowDeletion, preserveMetadata, conflictPolicy, pairId - - sourceEndpoint, targetEndpoint - - schedule - - lastRun, lastResult, lastError - - Does NOT own: - - dialogs/widgets - - MsgBox - - DB connections/cursors - """ - id: Optional[int] = None name: str = "" enabled: bool = True @@ -333,10 +258,6 @@ class Job: lastResult: Optional[str] = None lastError: Optional[str] = None - # ------------------------------------------------------------------ - # Validation & scheduling - # ------------------------------------------------------------------ - def validate(self) -> List[str]: errs: List[str] = [] if not self.name.strip(): @@ -350,7 +271,6 @@ def validate(self) -> List[str]: errs.extend(self.targetEndpoint.validate("target")) errs.extend(self.schedule.validate()) - # Basic constraints d = (self.direction or "").lower() if d not in ("unidirectional", "bidirectional"): errs.append("Job direction must be 'unidirectional' or 'bidirectional'.") @@ -371,10 +291,6 @@ def next_run_at(self, now: Optional[datetime] = None) -> Optional[datetime]: return None return self.schedule.next_run_at(now) - # ------------------------------------------------------------------ - # Execution (delegated) - # ------------------------------------------------------------------ - def run( self, *, @@ -383,19 +299,6 @@ def run( bidirectional_func: Optional[Callable[..., Tuple[bool, JsonDict]]] = None, logger: Optional[Callable[[str, str], None]] = None, ) -> JobRunResult: - """ - Execute job using injected runtime functions. - - - copy_func: should perform unidirectional copy, signature compatible with: - copy_func(src, dst, preserve_metadata=bool, allow_deletion=bool) -> bool - (matches your FileSystem.copy usage) - - bidirectional_func: should perform bidi sync, signature: - bidirectional_func(job: Job, run_id: Optional[int]) -> (ok, stats) - NOTE: run_id is NOT managed here; JobRunner should pass run_id. - - logger: (message, level) callback; level examples: "debug","info","warning","error" - - Returns JobRunResult with ok/fail + stats + timestamps. - """ now_dt = now or datetime.now(timezone.utc) started_at = now_dt.isoformat() @@ -406,7 +309,6 @@ def _log(msg: str, level: str = "info") -> None: except Exception: pass - # Validate first (no UI) errs = self.validate() if errs: msg = "; ".join(errs) @@ -479,19 +381,7 @@ def _log(msg: str, level: str = "info") -> None: stats=stats or {}, ) - # ------------------------------------------------------------------ - # Serialization helpers - # ------------------------------------------------------------------ - def to_row_dicts(self) -> Dict[str, Any]: - """ - Serialize into DB-ready row dicts: - - job_row: fields for jobs table - - endpoint_rows: list of fields for endpoints table (excluding jobId) - - schedule_row: fields for schedule table (excluding jobId) - - JobStore/Runner can add jobId and persist. - """ job_row = { "id": self.id, "name": self.name, @@ -515,11 +405,9 @@ def to_row_dicts(self) -> Dict[str, Any]: sched_row = { "enabled": 1 if self.schedule.enabled else 0, "everyMinutes": int(self.schedule.everyMinutes), - # nextRunAt / lastScheduledRunAt can be managed by JobRunner "nextRunAt": self.schedule.nextRunAt, "lastScheduledRunAt": self.schedule.lastScheduledRunAt, - # windows not persisted yet by default - # "windows": json.dumps(self.schedule.windows) ... + "windows": json.dumps(self.schedule.windows or {}) if self.schedule.windows else None, } return { @@ -534,11 +422,6 @@ def from_db_rows( endpoint_rows: Iterable[Mapping[str, Any]], schedule_row: Optional[Mapping[str, Any]] = None, ) -> "Job": - """ - Build Job from DB rows (jobs + endpoints + schedule). - - This matches your current schema and your current in-memory shape. - """ j = Job( id=int(job_row.get("id")) if job_row.get("id") is not None else None, name=str(job_row.get("name") or ""), @@ -554,7 +437,6 @@ def from_db_rows( lastError=job_row.get("lastError"), ) - # Endpoints by role src = None tgt = None for r in endpoint_rows: @@ -568,12 +450,17 @@ def from_db_rows( j.sourceEndpoint = src or Endpoint() j.targetEndpoint = tgt or Endpoint() - # Schedule if schedule_row: windows: JsonDict = {} - # If you later add a windows column, you can parse it here. - # raw_windows = schedule_row.get("windows") - # if raw_windows: ... + raw_windows = schedule_row.get("windows") + if raw_windows: + try: + parsed = json.loads(raw_windows) + if isinstance(parsed, dict): + windows = parsed + except Exception: + windows = {} + j.schedule = Schedule( enabled=bool(schedule_row.get("enabled", 0)), everyMinutes=int(schedule_row.get("everyMinutes", 60) or 60), @@ -582,16 +469,11 @@ def from_db_rows( lastScheduledRunAt=schedule_row.get("lastScheduledRunAt"), ) else: - j.schedule = Schedule(enabled=False, everyMinutes=60) + j.schedule = Schedule(enabled=False, everyMinutes=60, windows={}) return j - # Convenience: current in-memory dict shape compatibility def to_legacy_dict(self) -> JsonDict: - """ - Produce the same job dict shape your current Replicator code expects. - Useful as an interim bridge while refactoring. - """ return { "id": self.id, "name": self.name, @@ -607,7 +489,7 @@ def to_legacy_dict(self) -> JsonDict: "schedule": { "enabled": self.schedule.enabled, "everyMinutes": self.schedule.everyMinutes, - # windows not exposed yet by current UI + "windows": self.schedule.windows, }, "lastRun": self.lastRun, "lastResult": self.lastResult, @@ -615,46 +497,20 @@ def to_legacy_dict(self) -> JsonDict: } +# --------------------------------------------------------------------------- +# JobStore (DB persistence) +# --------------------------------------------------------------------------- class JobStore: - """SQLite persistence for Job domain objects. - - This class is UI-agnostic and contains no Qt dependencies. - - It targets the schema currently defined in `replicator.py`: - - jobs - - endpoints - - schedule - - Notes: - - This store does not write runs/conflicts/file_state. - - It upserts endpoints by (jobId, role) and schedule by (jobId). - """ + """SQLite persistence for Job domain objects (UI-agnostic).""" def __init__(self, db: Any): - """Create a JobStore. - - Preferred dependency is `core.database.sqlite.SQLite` (wrapper). - - Accepts either: - - a `SQLite` instance (preferred) - - a `sqlite3.Connection` - - a callable that returns `sqlite3.Connection` - - The fallback connection path exists only for backwards compatibility. - """ self._db = db - # ------------------------------- - # Connection helpers - # ------------------------------- - def _is_core_sqlite(self) -> bool: - # Avoid importing Qt here; we only check for the wrapper API. return SQLite is not None and isinstance(self._db, SQLite) def _conn(self) -> sqlite3.Connection: - """Fallback: get raw sqlite3.Connection.""" if isinstance(self._db, sqlite3.Connection): return self._db if callable(self._db): @@ -699,7 +555,6 @@ def _insert(self, table: str, data: Dict[str, Any]) -> int: def _update(self, table: str, data: Dict[str, Any], where: str, params: Any) -> None: if self._is_core_sqlite(): - # wrapper uses named binding for update WHERE params if not isinstance(params, dict): raise ValueError("JobStore._update with core SQLite requires dict params") self._db.update(table, data, where, params) # type: ignore[union-attr] @@ -714,18 +569,20 @@ def _upsert(self, table: str, data: Dict[str, Any], conflict_columns: List[str], if self._is_core_sqlite(): self._db.upsert(table, data, conflict_columns, update_columns) # type: ignore[union-attr] return - # Fallback path: build an INSERT..ON CONFLICT statement. + keys = list(data.keys()) cols = ", ".join([f'"{k}"' for k in keys]) placeholders = ", ".join(["?" for _ in keys]) conflict = ", ".join([f'"{c}"' for c in conflict_columns]) if update_columns is None: update_columns = [k for k in keys if k not in conflict_columns] + if update_columns: set_clause = ", ".join([f'"{k}"=excluded."{k}"' for k in update_columns]) sql = f'INSERT INTO "{table}" ({cols}) VALUES ({placeholders}) ON CONFLICT({conflict}) DO UPDATE SET {set_clause};' else: sql = f'INSERT INTO "{table}" ({cols}) VALUES ({placeholders}) ON CONFLICT({conflict}) DO NOTHING;' + conn = self._conn() conn.execute(sql, tuple(data[k] for k in keys)) @@ -739,7 +596,6 @@ def _delete(self, table: str, where: str, params: Any) -> None: def _transaction(self): if self._is_core_sqlite(): return self._db.transaction() # type: ignore[union-attr] - # sqlite3.Connection supports context manager transactions return self._conn() # ------------------------------- @@ -775,8 +631,6 @@ def fetch_schedule_row(self, job_id: int) -> Optional[Dict[str, Any]]: # ------------------------------- def upsert(self, job: Job) -> int: - """Insert or update a job + its endpoints + schedule. Returns job id.""" - row_dicts = job.to_row_dicts() job_row: Dict[str, Any] = row_dicts["job_row"] endpoint_rows: List[Dict[str, Any]] = row_dicts["endpoint_rows"] @@ -805,7 +659,7 @@ def upsert(self, job: Job) -> int: job_id = self._insert("jobs", data) job.id = job_id - # --- endpoints --- + # --- endpoints (unique: jobId+role) --- for ep in endpoint_rows: role = ep.get("role") if role not in ("source", "target"): @@ -823,23 +677,28 @@ def upsert(self, job: Job) -> int: "sshKey": ep.get("sshKey"), "options": ep.get("options"), } - # Unique index on (jobId, role) - self._upsert("endpoints", ep_data, ["jobId", "role"], update_columns=[ - "type", "location", "port", "guest", "username", "password", "useKey", "sshKey", "options" - ]) - - # --- schedule --- + self._upsert( + "endpoints", + ep_data, + ["jobId", "role"], + update_columns=["type", "location", "port", "guest", "username", "password", "useKey", "sshKey", "options"], + ) + + # --- schedule (unique: jobId) --- s_data = { "jobId": job_id, "enabled": int(schedule_row.get("enabled") or 0), "everyMinutes": int(schedule_row.get("everyMinutes") or 60), "nextRunAt": schedule_row.get("nextRunAt"), "lastScheduledRunAt": schedule_row.get("lastScheduledRunAt"), + "windows": schedule_row.get("windows"), } - # schedule.jobId is UNIQUE - self._upsert("schedule", s_data, ["jobId"], update_columns=[ - "enabled", "everyMinutes", "nextRunAt", "lastScheduledRunAt" - ]) + self._upsert( + "schedule", + s_data, + ["jobId"], + update_columns=["enabled", "everyMinutes", "nextRunAt", "lastScheduledRunAt", "windows"], + ) return int(job.id or 0) diff --git a/src/replicator/migration.py b/src/replicator/migration.py index 8fec9b8..4986972 100644 --- a/src/replicator/migration.py +++ b/src/replicator/migration.py @@ -1,4 +1,3 @@ - #!/usr/bin/env python3 # src/replicator/migration.py @@ -48,8 +47,6 @@ def get_meta(self, key: str, default: Optional[str] = None) -> Optional[str]: def set_meta(self, key: str, value: Optional[str]) -> None: """Upsert a value into the `meta` table.""" - # meta table is created by migration 0003, but this is called later - # (maintenance). If a user runs an older DB, this will no-op safely. try: with self._db.transaction(): exists = self._db.scalar("SELECT 1 FROM meta WHERE key = ? LIMIT 1", (key,)) @@ -257,4 +254,12 @@ def _migrations(self) -> List[Tuple[str, List[str]]]: """, ], ), + ( + "0004_schedule_windows", + [ + # Store schedule windows as JSON (dict weekday -> list[{start,end},...]) + # This is the minimal schema required to support your UI scheduling window editor. + "ALTER TABLE schedule ADD COLUMN windows TEXT NULL;", + ], + ), ] diff --git a/src/replicator/replicator.py b/src/replicator/replicator.py index 993ea16..cb6a1cd 100644 --- a/src/replicator/replicator.py +++ b/src/replicator/replicator.py @@ -6,7 +6,6 @@ from typing import Optional, Any, Dict, List import os -import sqlite3 import json import shutil @@ -29,6 +28,8 @@ ) from .ui import JobDialog, ScheduleDialog +from .migration import Migration +from .job import Job, Endpoint, Schedule, JobStore try: from core.helper import Helper @@ -45,249 +46,6 @@ from filesystem.filesystem import FileSystem from database.sqlite import SQLite -# ------------------------------------------------------------------ -# ReplicatorDB: SQLite storage/migrations -# ------------------------------------------------------------------ -class ReplicatorDB: - def __init__(self, db_path: str): - self._db_path = db_path - self._conn: Optional[sqlite3.Connection] = None - - def connect(self): - if self._conn is not None: - return self._conn - conn = sqlite3.connect(self._db_path) - conn.row_factory = sqlite3.Row - conn.execute("PRAGMA foreign_keys = ON") - self._conn = conn - return conn - - def execute(self, sql: str, params: tuple = ()): - conn = self.connect() - cur = conn.execute(sql, params) - conn.commit() - return cur - - def query_all(self, sql: str, params: tuple = ()) -> List[sqlite3.Row]: - conn = self.connect() - cur = conn.execute(sql, params) - return cur.fetchall() - - def query_one(self, sql: str, params: tuple = ()) -> Optional[sqlite3.Row]: - conn = self.connect() - cur = conn.execute(sql, params) - return cur.fetchone() - - def get_meta(self, key: str, default: Optional[str] = None) -> Optional[str]: - row = self.query_one("SELECT value FROM meta WHERE key = ?", (key,)) - if not row: - return default - return row["value"] - - def set_meta(self, key: str, value: Optional[str]) -> None: - conn = self.connect() - with conn: - row = conn.execute("SELECT 1 FROM meta WHERE key = ?", (key,)).fetchone() - if row: - conn.execute("UPDATE meta SET value = ? WHERE key = ?", (value, key)) - else: - conn.execute("INSERT INTO meta (key, value) VALUES (?, ?)", (key, value)) - - def ensure_created_and_migrated(self): - # Ensure parent dir exists - db_dir = os.path.dirname(self._db_path) - os.makedirs(db_dir, exist_ok=True) - conn = self.connect() - # Schema migrations table - conn.execute(""" - CREATE TABLE IF NOT EXISTS schema_migrations ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - created DATETIME DEFAULT CURRENT_TIMESTAMP, - modified DATETIME DEFAULT CURRENT_TIMESTAMP, - name TEXT NOT NULL UNIQUE - ); - """) - # Try to create modified trigger for schema_migrations (best effort) - try: - conn.execute(""" - CREATE TRIGGER trg_schema_migrations_modified - BEFORE UPDATE ON schema_migrations - FOR EACH ROW - BEGIN - UPDATE schema_migrations SET modified = CURRENT_TIMESTAMP WHERE id = OLD.id; - END; - """) - except Exception: - pass - - # List of migrations (name, list-of-sql) - migrations = [ - ("0001_init", [ - # jobs - """ - CREATE TABLE IF NOT EXISTS jobs ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - created DATETIME DEFAULT CURRENT_TIMESTAMP, - modified DATETIME DEFAULT CURRENT_TIMESTAMP, - name TEXT NOT NULL, - enabled INTEGER NOT NULL DEFAULT 1, - mode TEXT NOT NULL DEFAULT 'mirror', - direction TEXT NOT NULL DEFAULT 'unidirectional', - allowDeletion INTEGER NOT NULL DEFAULT 0, - preserveMetadata INTEGER NOT NULL DEFAULT 1, - pairId TEXT NULL, - conflictPolicy TEXT NOT NULL DEFAULT 'newest', - lastRun TEXT NULL, - lastResult TEXT NULL, - lastError TEXT NULL - ); - """, - "CREATE INDEX IF NOT EXISTS idx_jobs_enabled ON jobs(enabled);", - # endpoints - """ - CREATE TABLE IF NOT EXISTS endpoints ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - created DATETIME DEFAULT CURRENT_TIMESTAMP, - modified DATETIME DEFAULT CURRENT_TIMESTAMP, - jobId INTEGER NOT NULL, - role TEXT NOT NULL, - type TEXT NOT NULL, - location TEXT NOT NULL, - port INTEGER NULL, - guest INTEGER NOT NULL DEFAULT 1, - username TEXT NULL, - password TEXT NULL, - useKey INTEGER NOT NULL DEFAULT 0, - sshKey TEXT NULL, - options TEXT NULL, - FOREIGN KEY(jobId) REFERENCES jobs(id) ON DELETE CASCADE - ); - """, - "CREATE UNIQUE INDEX IF NOT EXISTS uq_endpoints_job_role ON endpoints(jobId, role);", - # schedule - """ - CREATE TABLE IF NOT EXISTS schedule ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - created DATETIME DEFAULT CURRENT_TIMESTAMP, - modified DATETIME DEFAULT CURRENT_TIMESTAMP, - jobId INTEGER NOT NULL UNIQUE, - enabled INTEGER NOT NULL DEFAULT 0, - everyMinutes INTEGER NOT NULL DEFAULT 60, - nextRunAt TEXT NULL, - lastScheduledRunAt TEXT NULL, - FOREIGN KEY(jobId) REFERENCES jobs(id) ON DELETE CASCADE - ); - """, - # runs - """ - CREATE TABLE IF NOT EXISTS runs ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - created DATETIME DEFAULT CURRENT_TIMESTAMP, - modified DATETIME DEFAULT CURRENT_TIMESTAMP, - jobId INTEGER NOT NULL, - startedAt TEXT NOT NULL, - endedAt TEXT NULL, - result TEXT NOT NULL, - message TEXT NULL, - stats TEXT NULL, - FOREIGN KEY(jobId) REFERENCES jobs(id) ON DELETE CASCADE - ); - """, - "CREATE INDEX IF NOT EXISTS idx_runs_job_started ON runs(jobId, startedAt);", - # file_state - """ - CREATE TABLE IF NOT EXISTS file_state ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - created DATETIME DEFAULT CURRENT_TIMESTAMP, - modified DATETIME DEFAULT CURRENT_TIMESTAMP, - jobId INTEGER NOT NULL, - side TEXT NOT NULL, - relPath TEXT NOT NULL, - size INTEGER NOT NULL DEFAULT 0, - mtime INTEGER NOT NULL DEFAULT 0, - hash TEXT NULL, - isDir INTEGER NOT NULL DEFAULT 0, - deleted INTEGER NOT NULL DEFAULT 0, - deletedAt TEXT NULL, - meta TEXT NULL, - FOREIGN KEY(jobId) REFERENCES jobs(id) ON DELETE CASCADE - ); - """, - "CREATE UNIQUE INDEX IF NOT EXISTS uq_file_state ON file_state(jobId, side, relPath);", - # conflicts - """ - CREATE TABLE IF NOT EXISTS conflicts ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - created DATETIME DEFAULT CURRENT_TIMESTAMP, - modified DATETIME DEFAULT CURRENT_TIMESTAMP, - jobId INTEGER NOT NULL, - runId INTEGER NULL, - relPath TEXT NOT NULL, - a_size INTEGER NULL, - a_mtime INTEGER NULL, - a_hash TEXT NULL, - b_size INTEGER NULL, - b_mtime INTEGER NULL, - b_hash TEXT NULL, - status TEXT NOT NULL DEFAULT 'open', - resolution TEXT NULL, - note TEXT NULL, - FOREIGN KEY(jobId) REFERENCES jobs(id) ON DELETE CASCADE, - FOREIGN KEY(runId) REFERENCES runs(id) ON DELETE SET NULL - ); - """, - "CREATE INDEX IF NOT EXISTS idx_conflicts_job_status ON conflicts(jobId, status);", - ]), - ("0002_file_state_last_seen", [ - "ALTER TABLE file_state ADD COLUMN lastSeenAt TEXT NULL;", - "ALTER TABLE file_state ADD COLUMN lastSeenRunId INTEGER NULL;", - ]), - ("0003_meta_kv", [ - """ - CREATE TABLE IF NOT EXISTS meta ( - key TEXT PRIMARY KEY, - created DATETIME DEFAULT CURRENT_TIMESTAMP, - modified DATETIME DEFAULT CURRENT_TIMESTAMP, - value TEXT NULL - ); - """, - ]), - ] - # Apply migrations in order - for name, stmts in migrations: - row = conn.execute("SELECT 1 FROM schema_migrations WHERE name = ?", (name,)).fetchone() - if not row: - try: - with conn: - for stmt in stmts: - conn.execute(stmt) - conn.execute("INSERT INTO schema_migrations (name) VALUES (?)", (name,)) - except Exception as e: - raise RuntimeError(f"Failed to apply migration {name}: {e}") - # Create modified triggers for all tables (best effort) - for tbl in ["jobs", "endpoints", "schedule", "runs", "file_state", "conflicts"]: - try: - self._ensure_modified_trigger(tbl) - except Exception: - pass - - def _ensure_modified_trigger(self, table_name: str): - # Try to create a BEFORE UPDATE trigger to set modified = CURRENT_TIMESTAMP - conn = self.connect() - trig_name = f"trg_{table_name}_modified" - # Try to drop if exists (to avoid duplicate triggers on repeated runs) - try: - conn.execute(f"DROP TRIGGER IF EXISTS {trig_name};") - except Exception: - pass - conn.execute(f""" - CREATE TRIGGER {trig_name} - BEFORE UPDATE ON {table_name} - FOR EACH ROW - BEGIN - UPDATE {table_name} SET modified = CURRENT_TIMESTAMP WHERE id = OLD.id; - END; - """) class Replicator(QMainWindow): @@ -351,58 +109,59 @@ def _scan_local_tree(self, root: str) -> Dict[str, Dict[str, Any]]: return out def _load_prev_file_state(self, job_id: int, side: str) -> Dict[str, Dict[str, Any]]: - rows = self._db.query_all( + rows = self._db.query( "SELECT relPath, size, mtime, isDir, deleted, deletedAt, lastSeenAt, lastSeenRunId FROM file_state WHERE jobId=? AND side=?", (job_id, side), ) prev: Dict[str, Dict[str, Any]] = {} - for r in rows: - prev[str(r["relPath"])] = { - "size": int(r["size"] or 0), - "mtime": int(r["mtime"] or 0), - "isDir": bool(r["isDir"]), - "deleted": bool(r["deleted"]), - "deletedAt": r["deletedAt"], - "lastSeenAt": r["lastSeenAt"], - "lastSeenRunId": r["lastSeenRunId"], + for r in rows or []: + prev[str(r.get("relPath"))] = { + "size": int(r.get("size") or 0), + "mtime": int(r.get("mtime") or 0), + "isDir": bool(r.get("isDir")), + "deleted": bool(r.get("deleted")), + "deletedAt": r.get("deletedAt"), + "lastSeenAt": r.get("lastSeenAt"), + "lastSeenRunId": r.get("lastSeenRunId"), } return prev def _persist_file_state(self, job_id: int, side: str, cur: Dict[str, Dict[str, Any]], run_id: Optional[int]) -> None: - """Upsert current snapshot into file_state and mark missing as deleted, using lastSeenAt/lastSeenRunId.""" - conn = self._db.connect() + # Use wrapper transaction; no raw connection needed. now_iso = datetime.now(timezone.utc).isoformat() - with conn: + with self._db.transaction(): # Upsert all current entries for rel, meta in cur.items(): is_dir = 1 if meta.get("isDir") else 0 size = int(meta.get("size", 0) or 0) mtime = int(meta.get("mtime", 0) or 0) - row = conn.execute( + + exists = self._db.scalar( "SELECT id FROM file_state WHERE jobId=? AND side=? AND relPath=?", (job_id, side, rel), - ).fetchone() - if row: - conn.execute( + ) + if exists: + self._db.execute( "UPDATE file_state SET size=?, mtime=?, isDir=?, deleted=0, deletedAt=NULL, lastSeenAt=?, lastSeenRunId=? WHERE jobId=? AND side=? AND relPath=?", (size, mtime, is_dir, now_iso, run_id, job_id, side, rel), ) else: - conn.execute( + self._db.execute( "INSERT INTO file_state (jobId, side, relPath, size, mtime, isDir, deleted, deletedAt, lastSeenAt, lastSeenRunId) VALUES (?,?,?,?,?,?,0,NULL,?,?)", (job_id, side, rel, size, mtime, is_dir, now_iso, run_id), ) + # Mark missing as deleted (single UPDATE) rels = list(cur.keys()) if rels: placeholders = ",".join(["?"] * len(rels)) - conn.execute( + self._db.execute( f"UPDATE file_state SET deleted=1, deletedAt=COALESCE(deletedAt, ?) WHERE jobId=? AND side=? AND deleted=0 AND relPath NOT IN ({placeholders})", (now_iso, job_id, side, *rels), ) else: # cur is empty: mark all as deleted for this job/side - conn.execute( + self._db.execute( "UPDATE file_state SET deleted=1, deletedAt=COALESCE(deletedAt, ?) WHERE jobId=? AND side=? AND deleted=0", (now_iso, job_id, side), ) @@ -453,7 +212,7 @@ def _record_conflict( ) -> None: try: # Avoid inserting duplicate open conflicts for the same path/note. - existing = self._db.query_one( + existing = self._db.one( "SELECT id FROM conflicts WHERE jobId=? AND relPath=? AND status='open' AND COALESCE(note,'')=COALESCE(?, '') ORDER BY id DESC LIMIT 1", (job_id, rel, note or ""), ) @@ -775,12 +534,18 @@ def __init__( data_dir = os.path.join(base_dir, "data") db_path = os.path.join(data_dir, "replicator.db") - self._db = ReplicatorDB(db_path) - self._db.ensure_created_and_migrated() + # --- Database (corePY SQLite wrapper) + migrations --- + self._db = SQLite(db_path=db_path) + self._migration = Migration(self._db, logger=self._logger) + self._migration.ensure() + + self._store = JobStore(self._db) + self._log(f"[Replicator] Database: {db_path}", level="debug") - self._log(f"[Replicator] Database migrations applied.", level="info") + self._log("[Replicator] Database migrations applied.", level="info") - self._jobs: List[Dict[str, Any]] = [] + # Domain jobs (Job objects) + self._jobs: List[Job] = [] self._table: Optional[QTableWidget] = None # After DB is ready, log a snapshot of DB layout/counts (non-verbose) @@ -920,13 +685,11 @@ def _selected_index(self) -> int: return rows[0].row() def _reload_jobs(self): - self._jobs = self._db_fetch_jobs() + self._jobs = self._store.fetch_all() self._refresh_table() def _save_jobs(self): - # No longer persists to config; upsert all jobs to DB (used by legacy code) - for job in self._jobs: - self._db_upsert_job(job) + return def _refresh_table(self): if not self._table: @@ -942,42 +705,66 @@ def _it(v: Any) -> QTableWidgetItem: item.setFlags(item.flags() ^ Qt.ItemIsEditable) return item - self._table.setItem(r, 0, _it(job.get("name", ""))) - self._table.setItem(r, 1, _it("On" if job.get("enabled", True) else "Off")) - # Source column - src_str = "" - src_ep = job.get("sourceEndpoint") - if isinstance(src_ep, dict): - src_str = f"{src_ep.get('type', 'local')}:{src_ep.get('location', '')}" - else: - src_str = job.get("source", "") + self._table.setItem(r, 0, _it(job.name)) + self._table.setItem(r, 1, _it("On" if job.enabled else "Off")) + + src_str = f"{job.sourceEndpoint.type}:{job.sourceEndpoint.location}" + tgt_str = f"{job.targetEndpoint.type}:{job.targetEndpoint.location}" self._table.setItem(r, 2, _it(src_str)) - # Target column - tgt_str = "" - tgt_ep = job.get("targetEndpoint") - if isinstance(tgt_ep, dict): - tgt_str = f"{tgt_ep.get('type', 'local')}:{tgt_ep.get('location', '')}" - else: - tgt_str = job.get("target", "") self._table.setItem(r, 3, _it(tgt_str)) - # Last run (column 5) - last_run = job.get("lastRun") - if last_run: - last_run_str = last_run - else: - last_run_str = "Never" - self._table.setItem(r, 4, _it(last_run_str)) - # Last result (column 6) - last_result = job.get("lastResult", "") - self._table.setItem(r, 5, _it(last_result)) + + last_run = job.lastRun + self._table.setItem(r, 4, _it(last_run if last_run else "Never")) + + self._table.setItem(r, 5, _it(job.lastResult or "")) + + def _job_from_legacy_dict(self, d: Dict[str, Any], *, existing_id: Optional[int] = None) -> Job: + src = d.get("sourceEndpoint") or {"type": "local", "location": d.get("source", ""), "auth": {}} + tgt = d.get("targetEndpoint") or {"type": "local", "location": d.get("target", ""), "auth": {}} + sched = d.get("schedule") or {} + + j = Job( + id=existing_id, + name=str(d.get("name") or ""), + enabled=bool(d.get("enabled", True)), + mode=str(d.get("mode") or "mirror"), + direction=str(d.get("direction") or "unidirectional"), + allowDeletion=bool(d.get("allowDeletion", False)), + preserveMetadata=bool(d.get("preserveMetadata", True)), + conflictPolicy=str(d.get("conflictPolicy") or "newest"), + pairId=d.get("pairId"), + lastRun=d.get("lastRun"), + lastResult=d.get("lastResult"), + lastError=d.get("lastError"), + ) + + j.sourceEndpoint = Endpoint( + type=str(src.get("type") or "local"), + location=str(src.get("location") or ""), + auth=dict(src.get("auth") or {}), + ) + j.targetEndpoint = Endpoint( + type=str(tgt.get("type") or "local"), + location=str(tgt.get("location") or ""), + auth=dict(tgt.get("auth") or {}), + ) + + windows = sched.get("windows") if isinstance(sched.get("windows"), dict) else {} + j.schedule = Schedule( + enabled=bool(sched.get("enabled", False)), + everyMinutes=int(sched.get("everyMinutes", 60) or 60), + windows=windows, + ) + + return j def _add_job(self): dlg = JobDialog(self) if dlg.exec_() == QDialog.Accepted: - new_job = dlg.value() - job_id = self._db_upsert_job(new_job) - new_job["id"] = job_id - self._jobs.append(new_job) + new_job_dict = dlg.value() + job_obj = self._job_from_legacy_dict(new_job_dict) + job_id = self._store.upsert(job_obj) + job_obj.id = job_id self._reload_jobs() def _edit_job(self): @@ -985,12 +772,13 @@ def _edit_job(self): if idx < 0: MsgBox.show(self, "Jobs", "Select a job to edit.", icon="info") return - dlg = JobDialog(self, self._jobs[idx]) + + current = self._jobs[idx] + dlg = JobDialog(self, current.to_legacy_dict()) if dlg.exec_() == QDialog.Accepted: - updated_job = dlg.value() - # preserve id - updated_job["id"] = self._jobs[idx].get("id") - self._db_upsert_job(updated_job) + updated_dict = dlg.value() + job_obj = self._job_from_legacy_dict(updated_dict, existing_id=current.id) + self._store.upsert(job_obj) self._reload_jobs() def _duplicate_job(self): @@ -998,43 +786,38 @@ def _duplicate_job(self): if idx < 0: MsgBox.show(self, "Jobs", "Select a job to duplicate.", icon="info") return - orig_job = self._jobs[idx] - new_job = dict(orig_job) - # Deep copy schedule if present - if "schedule" in orig_job and isinstance(orig_job["schedule"], dict): - new_job["schedule"] = dict(orig_job["schedule"]) - name = new_job.get("name") - if name: - new_job["name"] = f"{name} (copy)" - else: - new_job["name"] = "Copy" - if "id" in new_job: - del new_job["id"] - job_id = self._db_upsert_job(new_job) - new_job["id"] = job_id + + orig = self._jobs[idx] + d = orig.to_legacy_dict() + d.pop("id", None) + d["name"] = f"{orig.name} (copy)" if orig.name else "Copy" + + job_obj = self._job_from_legacy_dict(d) + self._store.upsert(job_obj) self._reload_jobs() - # Select the new row + if self._table: - new_row = self._table.rowCount() - 1 - self._table.selectRow(new_row) + # Select the newest row (best-effort) + self._table.selectRow(self._table.rowCount() - 1) def _delete_job(self): idx = self._selected_index() if idx < 0: MsgBox.show(self, "Jobs", "Select a job to delete.", icon="info") return + + job = self._jobs[idx] choice = MsgBox.show( self, title="Delete", - message=f"Delete job '{self._jobs[idx].get('name', '')}'?", + message=f"Delete job '{job.name}'?", icon="question", buttons=("Cancel", "Delete"), default="Cancel", ) if choice == "Delete": - job_id = self._jobs[idx].get("id") - if job_id: - self._db_delete_job(job_id) + if job.id: + self._store.delete(int(job.id)) self._reload_jobs() def _edit_schedule(self): @@ -1042,11 +825,14 @@ def _edit_schedule(self): if idx < 0: MsgBox.show(self, "Jobs", "Select a job to edit schedule.", icon="info") return - dlg = ScheduleDialog(self, job=self._jobs[idx]) + + current = self._jobs[idx] + dlg = ScheduleDialog(self, job=current.to_legacy_dict()) if dlg.exec_() == QDialog.Accepted: - job = self._jobs[idx] - job["schedule"] = dlg.value() - self._db_upsert_job(job) + d = current.to_legacy_dict() + d["schedule"] = dlg.value() + job_obj = self._job_from_legacy_dict(d, existing_id=current.id) + self._store.upsert(job_obj) self._reload_jobs() def _run_with_ui_feedback(self): @@ -1077,22 +863,22 @@ def run(self) -> bool: verbose_db = False self._db_debug_snapshot(verbose=verbose_db) - jobs = self._jobs + jobs: List[Job] = self._jobs if not jobs: self._log("[Replicator] No jobs configured.", level="warning") return False all_ok = True for job in jobs: - if not bool(job.get("enabled", True)): - self._log(f"[Replicator] Skipping disabled job '{job.get('name') or 'Unnamed'}'.", level="info") + if not bool(job.enabled): + self._log(f"[Replicator] Skipping disabled job '{job.name or 'Unnamed'}'.", level="info") continue ok = self._run_job(job) all_ok = all_ok and ok # Run DB maintenance at most once per day. try: - last = self._db.get_meta("maintenance.lastRunAt") + last = self._migration.get_meta("maintenance.lastRunAt") should = True if last: try: @@ -1109,46 +895,31 @@ def run(self) -> bool: return all_ok - def _run_job(self, job: Dict[str, Any]) -> bool: - name = job.get("name") or "Unnamed" - if not bool(job.get("enabled", True)): + def _run_job(self, job: Job) -> bool: + name = job.name or "Unnamed" + if not bool(job.enabled): self._log(f"[Replicator] Job '{name}' is disabled; skipping.", level="info") return True - job_id = job.get("id") - # Determine src/dst and types - src_ep = job.get("sourceEndpoint") - if isinstance(src_ep, dict): - src = src_ep.get("location", "") - src_type = src_ep.get("type", "local") - else: - src = job.get("source") or "" - src_type = "local" - dst_ep = job.get("targetEndpoint") - if isinstance(dst_ep, dict): - dst = dst_ep.get("location", "") - dst_type = dst_ep.get("type", "local") - else: - dst = job.get("target") or "" - dst_type = "local" - allow_deletion = bool(job.get("allowDeletion", False)) - preserve_metadata = bool(job.get("preserveMetadata", True)) - mode = job.get("mode", "mirror") - direction = job.get("direction", "unidirectional") - schedule = job.get("schedule", {}) + job_id = job.id + src = job.sourceEndpoint.location + src_type = job.sourceEndpoint.type + dst = job.targetEndpoint.location + dst_type = job.targetEndpoint.type + allow_deletion = bool(job.allowDeletion) + preserve_metadata = bool(job.preserveMetadata) + mode = job.mode + direction = job.direction + schedule = job.schedule.__dict__ if job.schedule else {} if not src or not dst: self._log(f"[Replicator] Job '{name}' invalid: missing source/target.", level="error") return False sched_str = "" - if schedule.get("enabled", False): - sched_str = f", schedule=Every {schedule.get('everyMinutes', 60)} min" + if getattr(job.schedule, "enabled", False): + sched_str = f", schedule=Every {getattr(job.schedule, 'everyMinutes', 60)} min" logline = f"[Replicator] Running job '{name}': {src} -> {dst} (mode={mode}, direction={direction}, delete={allow_deletion}, meta={preserve_metadata}{sched_str}" - if isinstance(src_ep, dict): - logline += f", srcType={src_type}" - if isinstance(dst_ep, dict): - logline += f", dstType={dst_type}" - logline += ")" + logline += f", srcType={src_type}, dstType={dst_type})" self._log(logline) # Insert a run record at start @@ -1156,11 +927,12 @@ def _run_job(self, job: Dict[str, Any]) -> bool: run_id = None bidi_stats = None try: - cur = self._db.execute( - "INSERT INTO runs (jobId, startedAt, result) VALUES (?, ?, ?)", - (job_id, started_at, "running"), - ) - run_id = cur.lastrowid + run_id = None + if job_id: + try: + run_id = int(self._db.insert("runs", {"jobId": int(job_id), "startedAt": started_at, "result": "running"})) + except Exception as e: + self._log(f"[Replicator] Failed to insert run record: {e}", level="warning") except Exception as e: self._log(f"[Replicator] Failed to insert run record: {e}", level="warning") @@ -1169,7 +941,7 @@ def _run_job(self, job: Dict[str, Any]) -> bool: ok = False try: if str(direction).lower() == "bidirectional": - ok, bidi_stats = self._run_job_bidirectional(job, run_id) + ok, bidi_stats = self._run_job_bidirectional(job.to_legacy_dict(), run_id) else: ok = self._fs.copy( src, @@ -1205,22 +977,24 @@ def _run_job(self, job: Dict[str, Any]) -> bool: # Persist lastRun and lastResult, refresh UI, save to DB now_str = datetime.now(timezone.utc).isoformat() - job["lastRun"] = now_str - job["lastResult"] = "ok" if ok else "fail" - job["lastError"] = None if ok else (job.get("lastError") or "Failed") - self._db_upsert_job(job) + job.lastRun = now_str + job.lastResult = "ok" if ok else "fail" + job.lastError = None if ok else (job.lastError or "Failed") + self._store.upsert(job) if run_id: try: - self._db.execute( - "UPDATE runs SET endedAt = ?, result = ?, message = ? WHERE id = ?", - (now_str, "ok" if ok else "fail", None if ok else (job.get("lastError") or "Failed"), run_id), + self._db.update( + "runs", + {"endedAt": now_str, "result": ("ok" if ok else "fail"), "message": (None if ok else (job.lastError or "Failed"))}, + "id = :id", + {"id": int(run_id)}, ) except Exception as e: self._log(f"[Replicator] Failed to update run record: {e}", level="warning") if self._table: self._reload_jobs() - self._log(f"[Replicator] Job '{name}' result: {'OK' if ok else 'FAIL'} (lastResult={job['lastResult']})", level="info" if ok else "warning") + self._log(f"[Replicator] Job '{job.name}' result: {'OK' if ok else 'FAIL'} (lastResult={job.lastResult})", level="info" if ok else "warning") return ok def _log(self, msg: str, level: str = "info", channel: str = "replicator") -> None: @@ -1235,21 +1009,17 @@ def _db_debug_snapshot(self, verbose: bool = False) -> None: If verbose=True, also logs the most recent rows for key tables. """ try: - conn = self._db.connect() - # List tables - tables = [r["name"] for r in conn.execute( - "SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' ORDER BY name" - ).fetchall()] + tables = [r["name"] for r in self._db.query("SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' ORDER BY name")] self._log(f"[Replicator][DB] Tables: {', '.join(tables) if tables else '(none)'}", level="debug") # Layout + counts for t in tables: try: - cols = conn.execute(f"PRAGMA table_info({t})").fetchall() - col_names = [c[1] for c in cols] # (cid, name, type, notnull, dflt_value, pk) - count = conn.execute(f"SELECT COUNT(*) FROM {t}").fetchone()[0] + cols = self._db.query(f"PRAGMA table_info({t})") + col_names = [c.get("name") for c in cols] + count = int(self._db.scalar(f"SELECT COUNT(*) FROM {t}") or 0) self._log(f"[Replicator][DB] {t}: columns={len(col_names)} rows={count}", level="debug") if verbose: self._log(f"[Replicator][DB] {t}: {', '.join(col_names)}", level="debug") @@ -1259,22 +1029,17 @@ def _db_debug_snapshot(self, verbose: bool = False) -> None: if not verbose: return - # Recent rows (lightweight, avoid dumping secrets) - def _safe_row(row: sqlite3.Row) -> Dict[str, Any]: - d = dict(row) - # redact sensitive fields if present - for k in ("password", "sshKey"): - if k in d and d[k]: - d[k] = "***" - return d - for t, order_col in (("jobs", "id"), ("runs", "id"), ("schedule", "id"), ("conflicts", "id")): if t in tables: try: - rows = conn.execute(f"SELECT * FROM {t} ORDER BY {order_col} DESC LIMIT 3").fetchall() + rows = self._db.query(f"SELECT * FROM {t} ORDER BY {order_col} DESC LIMIT 3") self._log(f"[Replicator][DB] {t}: latest {len(rows)} row(s)", level="debug") for r in rows: - self._log(f"[Replicator][DB] {t}: {_safe_row(r)}", level="debug") + dr = dict(r) + for k in ("password", "sshKey"): + if k in dr and dr[k]: + dr[k] = "***" + self._log(f"[Replicator][DB] {t}: {dr}", level="debug") except Exception as e: self._log(f"[Replicator][DB] Failed reading latest rows from {t}: {e}", level="warning") @@ -1282,200 +1047,6 @@ def _safe_row(row: sqlite3.Row) -> Dict[str, Any]: self._log(f"[Replicator][DB] Snapshot failed: {e}", level="warning") -# ------------------------------------------------------------------ -# DB-backed job persistence methods -# ------------------------------------------------------------------ - def _db_fetch_jobs(self) -> List[Dict[str, Any]]: - # Query jobs ordered by id - rows = self._db.query_all("SELECT * FROM jobs ORDER BY id ASC") - jobs: List[Dict[str, Any]] = [] - for row in rows: - job = dict(row) - job["id"] = row["id"] - enabled_val = row["enabled"] if "enabled" in row.keys() else 1 - job["enabled"] = bool(enabled_val) - # Endpoints - eps = self._db.query_all("SELECT * FROM endpoints WHERE jobId = ?", (row["id"],)) - for ep in eps: - ep_d = dict(ep) - auth = {} - # Compose auth dict - if ep_d["type"] == "local": - auth = {} - elif ep_d["type"] in ("smb", "ftp"): - auth = { - "guest": bool(ep_d.get("guest", 1)), - "username": ep_d.get("username") or "", - "password": ep_d.get("password") or "", - } - if ep_d["type"] == "ftp": - auth["port"] = ep_d.get("port") or 21 - elif ep_d["type"] == "ssh": - auth = { - "useKey": bool(ep_d.get("useKey", 0)), - "username": ep_d.get("username") or "", - "password": ep_d.get("password") or "", - "port": ep_d.get("port") or 22, - "key": ep_d.get("sshKey") or "", - } - # Add any JSON options - if ep_d.get("options"): - try: - auth.update(json.loads(ep_d["options"])) - except Exception: - pass - ep_obj = { - "type": ep_d["type"], - "location": ep_d["location"], - "auth": auth, - } - if ep_d["role"] == "source": - job["sourceEndpoint"] = ep_obj - job["source"] = ep_d["location"] - elif ep_d["role"] == "target": - job["targetEndpoint"] = ep_obj - job["target"] = ep_d["location"] - # Schedule - sched_row = self._db.query_one("SELECT * FROM schedule WHERE jobId = ?", (row["id"],)) - if sched_row: - job["schedule"] = { - "enabled": bool(sched_row["enabled"]), - "everyMinutes": sched_row["everyMinutes"], - } - else: - job["schedule"] = {"enabled": False, "everyMinutes": 60} - # lastRun/lastResult/lastError - job["lastRun"] = row["lastRun"] - job["lastResult"] = row["lastResult"] - job["lastError"] = row["lastError"] - jobs.append(job) - return jobs - - def _db_upsert_job(self, job: Dict[str, Any]) -> int: - - # Normalize enabled - enabled = 1 if bool(job.get("enabled", True)) else 0 - - # Insert or update jobs row, endpoints, schedule (transaction) - conn = self._db.connect() - with conn: - # Upsert job - fields = [ - "name", "enabled", "mode", "direction", "allowDeletion", "preserveMetadata", - "pairId", "conflictPolicy", "lastRun", "lastResult", "lastError" - ] - values = [ - job.get("name"), - enabled, - job.get("mode", "mirror"), - job.get("direction", "unidirectional"), - 1 if job.get("allowDeletion", False) else 0, - 1 if job.get("preserveMetadata", True) else 0, - job.get("pairId"), - job.get("conflictPolicy", "newest"), - job.get("lastRun"), - job.get("lastResult"), - job.get("lastError"), - ] - if job.get("id"): - # UPDATE - set_clause = ", ".join(f"{f}=?" for f in fields) - conn.execute( - f"UPDATE jobs SET {set_clause} WHERE id = ?", - tuple(values) + (job["id"],) - ) - job_id = job["id"] - else: - # INSERT - placeholders = ", ".join("?" for _ in fields) - cur = conn.execute( - f"INSERT INTO jobs ({', '.join(fields)}) VALUES ({placeholders})", - tuple(values) - ) - job_id = cur.lastrowid - job["id"] = job_id - - # Endpoints (upsert by (jobId, role)) - for role in ("source", "target"): - ep = job.get("sourceEndpoint") if role == "source" else job.get("targetEndpoint") - if not isinstance(ep, dict): - continue - ep_type = ep.get("type", "local") - location = ep.get("location", "") - auth = ep.get("auth", {}) or {} - # Compose columns - port = None - guest = 1 - username = None - password = None - useKey = 0 - sshKey = None - options = {} - if ep_type == "local": - pass - elif ep_type in ("smb", "ftp"): - guest = 1 if auth.get("guest", True) else 0 - username = auth.get("username") - password = auth.get("password") - if ep_type == "ftp": - port = int(auth.get("port", 21)) - elif ep_type == "ssh": - useKey = 1 if auth.get("useKey", False) else 0 - username = auth.get("username") - password = auth.get("password") - port = int(auth.get("port", 22)) - sshKey = auth.get("key") - # Store any extra keys in options - known_keys = {"guest", "username", "password", "port", "useKey", "key"} - for k, v in auth.items(): - if k not in known_keys: - options[k] = v - # Upsert: try update first, else insert - ep_row = conn.execute( - "SELECT id FROM endpoints WHERE jobId = ? AND role = ?", - (job_id, role) - ).fetchone() - ep_fields = [ - "jobId", "role", "type", "location", "port", "guest", "username", "password", "useKey", "sshKey", "options" - ] - ep_values = [ - job_id, role, ep_type, location, port, guest, username, password, useKey, sshKey, - json.dumps(options) if options else None - ] - if ep_row: - set_clause = ", ".join(f"{f}=?" for f in ep_fields[2:]) # skip jobId, role - conn.execute( - f"UPDATE endpoints SET {set_clause} WHERE jobId=? AND role=?", - tuple(ep_values[2:]) + (job_id, role) - ) - else: - placeholders = ", ".join("?" for _ in ep_fields) - conn.execute( - f"INSERT INTO endpoints ({', '.join(ep_fields)}) VALUES ({placeholders})", - tuple(ep_values) - ) - # Schedule - sched = job.get("schedule") - if sched: - sched_row = conn.execute( - "SELECT id FROM schedule WHERE jobId = ?", (job_id,) - ).fetchone() - enabled = 1 if sched.get("enabled", False) else 0 - every = int(sched.get("everyMinutes", 60)) - if sched_row: - conn.execute( - "UPDATE schedule SET enabled=?, everyMinutes=? WHERE jobId=?", - (enabled, every, job_id) - ) - else: - conn.execute( - "INSERT INTO schedule (jobId, enabled, everyMinutes) VALUES (?, ?, ?)", - (job_id, enabled, every) - ) - return job.get("id") - - def _db_delete_job(self, job_id: int) -> None: - self._db.execute("DELETE FROM jobs WHERE id = ?", (job_id,)) def _db_maintenance(self) -> None: @@ -1500,17 +1071,16 @@ def _db_maintenance(self) -> None: prune_conflict_days, prune_deleted_state_days = 90, 30 do_vacuum = False - conn = self._db.connect() now = datetime.now(timezone.utc) now_iso = now.isoformat() # Runs: keep last N per job + prune anything older than keep_days try: - job_ids = [r[0] for r in conn.execute("SELECT id FROM jobs").fetchall()] - with conn: + job_ids = [int(r.get("id")) for r in (self._db.query("SELECT id FROM jobs") or [])] + with self._db.transaction(): for jid in job_ids: if keep_last > 0: - conn.execute( + self._db.execute( """ DELETE FROM runs WHERE jobId = ? @@ -1522,7 +1092,7 @@ def _db_maintenance(self) -> None: ) if keep_days > 0: - conn.execute( + self._db.execute( "DELETE FROM runs WHERE julianday(created) < julianday('now', ?) ", (f'-{keep_days} days',), ) @@ -1532,8 +1102,8 @@ def _db_maintenance(self) -> None: # Conflicts: keep all open; prune non-open older than prune_conflict_days try: if prune_conflict_days > 0: - with conn: - conn.execute( + with self._db.transaction(): + self._db.execute( "DELETE FROM conflicts WHERE status <> 'open' AND julianday(created) < julianday('now', ?) ", (f'-{prune_conflict_days} days',), ) @@ -1543,8 +1113,8 @@ def _db_maintenance(self) -> None: # file_state: prune deleted entries that have been deleted for a long time try: if prune_deleted_state_days > 0: - with conn: - conn.execute( + with self._db.transaction(): + self._db.execute( "DELETE FROM file_state WHERE deleted = 1 AND deletedAt IS NOT NULL AND julianday(deletedAt) < julianday('now', ?) ", (f'-{prune_deleted_state_days} days',), ) @@ -1553,10 +1123,10 @@ def _db_maintenance(self) -> None: # SQLite maintenance: optimize; optionally vacuum try: - conn.execute("PRAGMA optimize;") + self._db.execute("PRAGMA optimize;") if do_vacuum: - conn.execute("VACUUM;") - self._db.set_meta("maintenance.lastRunAt", now_iso) + self._db.execute("VACUUM;") + self._migration.set_meta("maintenance.lastRunAt", now_iso) self._log("[Replicator][DB] Maintenance completed.", level="debug") except Exception as e: self._log(f"[Replicator][DB] Maintenance failed: {e}", level="warning") diff --git a/src/replicator/ui.py b/src/replicator/ui.py index 880754b..3151c11 100644 --- a/src/replicator/ui.py +++ b/src/replicator/ui.py @@ -3,9 +3,9 @@ from __future__ import annotations -from typing import Optional, Any, Dict +from typing import Optional, Any, Dict, List, Tuple -from PyQt5.QtCore import Qt +from PyQt5.QtCore import Qt, QTime from PyQt5.QtWidgets import ( QWidget, QVBoxLayout, @@ -22,6 +22,7 @@ QFrame, QSizePolicy, QLayout, + QTimeEdit, ) try: @@ -50,7 +51,7 @@ def __init__(self, parent=None, job: Optional[Dict[str, Any]] = None): layout.setSizeConstraint(QLayout.SetMinimumSize) # ------------------------------ - # Name row (full width) + # Name row # ------------------------------ name_row = QHBoxLayout() name_row.addWidget(QLabel("Name")) @@ -63,24 +64,18 @@ def _get_endpoint(key_endpoint: str, key_str: str, default_type: str = "local") ep = job.get(key_endpoint) if isinstance(ep, dict): return dict(ep) - return { - "type": default_type, - "location": job.get(key_str, ""), - "auth": {}, - } + return {"type": default_type, "location": job.get(key_str, ""), "auth": {}} self._source_ep = _get_endpoint("sourceEndpoint", "source", "local") self._target_ep = _get_endpoint("targetEndpoint", "target", "local") # ------------------------------ - # Two-column area: Source / Destination + # Two-column Source / Destination # ------------------------------ cols = QHBoxLayout() cols.setSpacing(20) - # Keep endpoint panels compact (avoid vertical stretching) cols.setAlignment(Qt.AlignTop) - # Bordered containers for better visual separation src_frame = QFrame() src_frame.setObjectName("EndpointFrame") src_frame.setFrameShape(QFrame.NoFrame) @@ -133,7 +128,6 @@ def _get_endpoint(key_endpoint: str, key_str: str, default_type: str = "local") self._target_auth_widgets, ) = self._build_endpoint(existing=self._target_ep) - # Endpoint row: Type + Location (+ Port for FTP/SSH) src_col.addWidget(self._source_endpoint_row) src_col.addWidget(self._source_auth_widget) @@ -161,7 +155,6 @@ def _get_endpoint(key_endpoint: str, key_str: str, default_type: str = "local") if idx >= 0: self.direction.setCurrentIndex(idx) - # Mode + Direction on same line, each taking half width mode_dir_row = QHBoxLayout() mode_wrap = QWidget() @@ -180,15 +173,12 @@ def _get_endpoint(key_endpoint: str, key_str: str, default_type: str = "local") dir_lay.addWidget(QLabel("Direction")) dir_lay.addWidget(self.direction, 1) - self.mode.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) - self.direction.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) - mode_dir_row.addWidget(mode_wrap, 1) mode_dir_row.addWidget(dir_wrap, 1) layout.addLayout(mode_dir_row) # ------------------------------ - # Other options (kept simple) + # Other options # ------------------------------ opts_row = QHBoxLayout() self.allow_deletion = QCheckBox("Allow deletion") @@ -202,6 +192,12 @@ def _get_endpoint(key_endpoint: str, key_str: str, default_type: str = "local") opts_row.addWidget(self.preserve_metadata) opts_row.addWidget(self.enabled) opts_row.addStretch(1) + + # Schedule button (UI stays here) + self._schedule_btn = QPushButton("Schedule…") + self._schedule_btn.clicked.connect(self._open_schedule) + opts_row.addWidget(self._schedule_btn) + layout.addLayout(opts_row) # ------------------------------ @@ -251,24 +247,26 @@ def _get_endpoint(key_endpoint: str, key_str: str, default_type: str = "local") initial=True, ) + def _open_schedule(self) -> None: + job = self._original_job if isinstance(self._original_job, dict) else {} + dlg = ScheduleDialog(self, job=job) + if dlg.exec_() == QDialog.Accepted: + # store schedule back into original job dict so value() preserves it + if not isinstance(self._original_job, dict): + self._original_job = {} + self._original_job["schedule"] = dlg.value() + # ------------------------------------------------------------------ # Endpoint UI # ------------------------------------------------------------------ def _build_endpoint(self, existing: Dict[str, Any]): - """Build endpoint widgets. - - Returns: - (type_combo, location_edit, port_spin, endpoint_row_widget, auth_widget, widgets_dict) - """ - # Top row: [Type] [Location] [Port (FTP/SSH)] type_combo = QComboBox() type_combo.addItems(["local", "smb", "ftp", "ssh"]) idx = type_combo.findText(existing.get("type", "local")) if idx >= 0: type_combo.setCurrentIndex(idx) type_combo.setFixedWidth(110) - # Allow _on_type_changed to access original config type_combo.setProperty("existing_type", existing.get("type", "local")) type_combo.setProperty("existing_auth", existing.get("auth", {}) or {}) @@ -277,7 +275,7 @@ def _build_endpoint(self, existing: Dict[str, Any]): port_spin = QSpinBox() port_spin.setRange(1, 65535) port_spin.setFixedWidth(110) - # Default ports (used when the type is FTP/SSH) + existing_type = existing.get("type", "local") existing_auth = existing.get("auth", {}) or {} if existing_type == "ftp": @@ -285,10 +283,8 @@ def _build_endpoint(self, existing: Dict[str, Any]): elif existing_type == "ssh": port_spin.setValue(int(existing_auth.get("port", 22) or 22)) else: - # Keep a sane default value even when hidden port_spin.setValue(21) - # Endpoint row widget with label: [Label] [Type] [Location] [Port] endpoint_fields = QWidget() fields_lay = QHBoxLayout(endpoint_fields) fields_lay.setContentsMargins(0, 0, 0, 0) @@ -304,8 +300,6 @@ def _build_endpoint(self, existing: Dict[str, Any]): endpoint_form.setSpacing(6) endpoint_form.addRow(endpoint_fields) - # Auth widget (shown only when non-local) - # Use a dedicated sub-container that shrinks/grows with its visible children. auth_widget = QFrame() auth_widget.setFrameShape(QFrame.NoFrame) auth_widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Maximum) @@ -334,7 +328,7 @@ def _build_endpoint(self, existing: Dict[str, Any]): smb_password.setEchoMode(QLineEdit.Password) smb_auth = existing.get("auth", {}) if existing.get("type") == "smb" else {} - smb_guest.setChecked(bool(smb_auth.get("guest", True))) # default guest ON + smb_guest.setChecked(bool(smb_auth.get("guest", True))) smb_username.setText(smb_auth.get("username", "")) smb_password.setText(smb_auth.get("password", "")) @@ -365,7 +359,7 @@ def _smb_guest_update(): ftp_password.setEchoMode(QLineEdit.Password) ftp_auth = existing.get("auth", {}) if existing.get("type") == "ftp" else {} - ftp_guest.setChecked(bool(ftp_auth.get("guest", True))) # default guest ON + ftp_guest.setChecked(bool(ftp_auth.get("guest", True))) ftp_username.setText(ftp_auth.get("username", "")) ftp_password.setText(ftp_auth.get("password", "")) @@ -403,7 +397,6 @@ def _ftp_guest_update(): ssh_username.setText(ssh_auth.get("username", "")) ssh_password.setText(ssh_auth.get("password", "")) ssh_key_text.setPlainText(ssh_auth.get("key", "")) - # Default: use key if no password provided ssh_use_key.setChecked(bool(ssh_auth.get("useKey", True if not ssh_password.text().strip() else False))) ssh_form.addRow(ssh_use_key) @@ -413,10 +406,8 @@ def _ftp_guest_update(): def _ssh_use_key_update(): use_key = ssh_use_key.isChecked() - # Username always visible ssh_user_lbl.setVisible(True) ssh_username.setVisible(True) - # Toggle password vs key ssh_pass_lbl.setVisible(not use_key) ssh_password.setVisible(not use_key) ssh_key_lbl.setVisible(use_key) @@ -425,39 +416,14 @@ def _ssh_use_key_update(): ssh_use_key.stateChanged.connect(_ssh_use_key_update) _ssh_use_key_update() - # Stacked display controlled by _on_type_changed - # We keep all wraps created and toggle visibility. auth_lay.addWidget(auth_title) auth_lay.addWidget(smb_wrap) auth_lay.addWidget(ftp_wrap) auth_lay.addWidget(ssh_wrap) - widgets["smb"] = { - "wrap": smb_wrap, - "guest": smb_guest, - "user_lbl": smb_user_lbl, - "username": smb_username, - "pass_lbl": smb_pass_lbl, - "password": smb_password, - } - widgets["ftp"] = { - "wrap": ftp_wrap, - "guest": ftp_guest, - "user_lbl": ftp_user_lbl, - "username": ftp_username, - "pass_lbl": ftp_pass_lbl, - "password": ftp_password, - } - widgets["ssh"] = { - "wrap": ssh_wrap, - "useKey": ssh_use_key, - "user_lbl": ssh_user_lbl, - "username": ssh_username, - "pass_lbl": ssh_pass_lbl, - "password": ssh_password, - "key_lbl": ssh_key_lbl, - "key": ssh_key_text, - } + widgets["smb"] = {"wrap": smb_wrap, "guest": smb_guest, "user_lbl": smb_user_lbl, "username": smb_username, "pass_lbl": smb_pass_lbl, "password": smb_password} + widgets["ftp"] = {"wrap": ftp_wrap, "guest": ftp_guest, "user_lbl": ftp_user_lbl, "username": ftp_username, "pass_lbl": ftp_pass_lbl, "password": ftp_password} + widgets["ssh"] = {"wrap": ssh_wrap, "useKey": ssh_use_key, "user_lbl": ssh_user_lbl, "username": ssh_username, "pass_lbl": ssh_pass_lbl, "password": ssh_password, "key_lbl": ssh_key_lbl, "key": ssh_key_text} return type_combo, location_edit, port_spin, endpoint_row, auth_widget, widgets @@ -472,7 +438,6 @@ def _on_type_changed( ): typ = type_combo.currentText() - # Placeholders placeholder = { "local": "Local path (e.g. /data or C:\\Data)", "smb": "SMB path (e.g. \\\\SERVER\\Share\\Folder)", @@ -481,21 +446,18 @@ def _on_type_changed( }.get(typ, "") location_edit.setPlaceholderText(placeholder) - # Port visibility + defaults if typ in ("ftp", "ssh"): port_spin.setVisible(True) existing_type = type_combo.property("existing_type") or "local" existing_auth = type_combo.property("existing_auth") or {} - # If initial load and the saved endpoint type matches, prefer saved port if initial and existing_type == typ: if typ == "ftp": port_spin.setValue(int(existing_auth.get("port", 21) or 21)) - else: # ssh + else: port_spin.setValue(int(existing_auth.get("port", 22) or 22)) else: - # When switching types, fix common wrong/default values cur = int(port_spin.value()) if typ == "ftp" and cur in (0, 22): port_spin.setValue(21) @@ -504,7 +466,6 @@ def _on_type_changed( else: port_spin.setVisible(False) - # Auth visibility: collapse container when hidden, resize to content when shown if typ == "local": auth_widget.setVisible(False) auth_widget.setMaximumHeight(0) @@ -512,7 +473,6 @@ def _on_type_changed( auth_widget.setVisible(True) auth_widget.setMaximumHeight(16777215) - # Toggle which auth panel is visible for key in ("smb", "ftp", "ssh"): if key in widgets and "wrap" in widgets[key]: widgets[key]["wrap"].setVisible(False) @@ -520,15 +480,12 @@ def _on_type_changed( if typ in ("smb", "ftp", "ssh"): widgets[typ]["wrap"].setVisible(True) - # Defaults: guest ON for non-local SMB/FTP when not configured if typ in ("smb", "ftp"): w = widgets[typ] if w["guest"].isChecked() is False: - # Only auto-enable guest if user/pass are empty if not w["username"].text().strip() and not w["password"].text().strip(): w["guest"].setChecked(True) - # Force re-layout so the auth container shrinks/grows immediately auth_widget.adjustSize() if auth_widget.parentWidget() is not None: auth_widget.parentWidget().adjustSize() @@ -556,7 +513,6 @@ def _on_ok(self): MsgBox.show(self, "Job", "Target location is required.", icon="warning") return - # Source auth validation if src_type in ("smb", "ftp"): w = self._source_auth_widgets[src_type] if not w["guest"].isChecked(): @@ -578,7 +534,6 @@ def _on_ok(self): MsgBox.show(self, "Job", "Source SSH password is required when not using a key.", icon="warning") return - # Target auth validation if tgt_type in ("smb", "ftp"): w = self._target_auth_widgets[tgt_type] if not w["guest"].isChecked(): @@ -607,12 +562,7 @@ def _on_ok(self): # ------------------------------------------------------------------ def value(self) -> Dict[str, Any]: - def _extract( - type_combo: QComboBox, - location_edit: QLineEdit, - port_spin: QSpinBox, - widgets: Dict[str, Any], - ) -> Dict[str, Any]: + def _extract(type_combo: QComboBox, location_edit: QLineEdit, port_spin: QSpinBox, widgets: Dict[str, Any]) -> Dict[str, Any]: typ = type_combo.currentText() location = location_edit.text().strip() auth: Dict[str, Any] = {} @@ -621,55 +571,25 @@ def _extract( auth = {} elif typ == "smb": w = widgets["smb"] - auth = { - "guest": bool(w["guest"].isChecked()), - "username": w["username"].text().strip(), - "password": w["password"].text(), - } + auth = {"guest": bool(w["guest"].isChecked()), "username": w["username"].text().strip(), "password": w["password"].text()} elif typ == "ftp": w = widgets["ftp"] - auth = { - "guest": bool(w["guest"].isChecked()), - "username": w["username"].text().strip(), - "password": w["password"].text(), - "port": int(port_spin.value()), - } + auth = {"guest": bool(w["guest"].isChecked()), "username": w["username"].text().strip(), "password": w["password"].text(), "port": int(port_spin.value())} elif typ == "ssh": w = widgets["ssh"] - auth = { - "useKey": bool(w["useKey"].isChecked()), - "username": w["username"].text().strip(), - "password": w["password"].text(), - "port": int(port_spin.value()), - "key": w["key"].toPlainText(), - } - - return { - "type": typ, - "location": location, - "auth": auth, - } - - source_ep = _extract( - self._source_type_combo, - self._source_location_edit, - self._source_port_spin, - self._source_auth_widgets, - ) - target_ep = _extract( - self._target_type_combo, - self._target_location_edit, - self._target_port_spin, - self._target_auth_widgets, - ) + auth = {"useKey": bool(w["useKey"].isChecked()), "username": w["username"].text().strip(), "password": w["password"].text(), "port": int(port_spin.value()), "key": w["key"].toPlainText()} + + return {"type": typ, "location": location, "auth": auth} + + source_ep = _extract(self._source_type_combo, self._source_location_edit, self._source_port_spin, self._source_auth_widgets) + target_ep = _extract(self._target_type_combo, self._target_location_edit, self._target_port_spin, self._target_auth_widgets) val: Dict[str, Any] = { "name": self.name.text().strip(), "sourceEndpoint": source_ep, "targetEndpoint": target_ep, - # Backward compatibility - "source": source_ep["location"], - "target": target_ep["location"], + "source": source_ep["location"], # backward compatibility + "target": target_ep["location"], # backward compatibility "allowDeletion": bool(self.allow_deletion.isChecked()), "preserveMetadata": bool(self.preserve_metadata.isChecked()), "mode": self.mode.currentText(), @@ -677,7 +597,7 @@ def _extract( "enabled": bool(self.enabled.isChecked()), } - # Preserve schedule fields if they existed previously + # Preserve schedule (and update if ScheduleDialog was used) if self._original_job and isinstance(self._original_job, dict): sched = self._original_job.get("schedule") if sched is not None: @@ -685,26 +605,51 @@ def _extract( return val + # ------------------------------------------------------------------ # Schedule Dialog # ------------------------------------------------------------------ class ScheduleDialog(QDialog): + """Schedule editor UI. + + Persists as: + schedule = { + "enabled": bool, + "everyMinutes": int, + "windows": { "0":[{"start":"22:00","end":"06:00"}], ... } + } + """ + + _DAYS: List[Tuple[int, str]] = [ + (0, "Monday"), + (1, "Tuesday"), + (2, "Wednesday"), + (3, "Thursday"), + (4, "Friday"), + (5, "Saturday"), + (6, "Sunday"), + ] + def __init__(self, parent=None, job: Optional[Dict[str, Any]] = None): super().__init__(parent) self.setWindowTitle("Schedule") self.setModal(True) job = job or {} - schedule = job.get("schedule", {}) + schedule = job.get("schedule", {}) if isinstance(job.get("schedule", {}), dict) else {} + + self._windows: Dict[str, Any] = schedule.get("windows", {}) if isinstance(schedule.get("windows", {}), dict) else {} layout = QVBoxLayout(self) layout.setAlignment(Qt.AlignTop) layout.setSizeConstraint(QLayout.SetMinimumSize) + form = QFormLayout() layout.addLayout(form) self.enabled = QCheckBox() self.enabled.setChecked(bool(schedule.get("enabled", False))) + self.every_minutes = QSpinBox() self.every_minutes.setRange(1, 10080) self.every_minutes.setValue(int(schedule.get("everyMinutes", 60))) @@ -712,26 +657,128 @@ def __init__(self, parent=None, job: Optional[Dict[str, Any]] = None): form.addRow("Enabled", self.enabled) form.addRow("Every minutes", self.every_minutes) + # Windows editor + win_frame = QFrame() + win_frame.setFrameShape(QFrame.NoFrame) + win_lay = QVBoxLayout(win_frame) + win_lay.setContentsMargins(0, 0, 0, 0) + win_lay.setSpacing(6) + + title = QLabel("Allowed run windows") + title.setStyleSheet("font-weight: 600;") + win_lay.addWidget(title) + + self._day_controls: Dict[int, Dict[str, Any]] = {} + + for wd, label in self._DAYS: + row = QHBoxLayout() + row.setSpacing(10) + + day_enabled = QCheckBox(label) + start = QTimeEdit() + end = QTimeEdit() + start.setDisplayFormat("HH:mm") + end.setDisplayFormat("HH:mm") + start.setTime(QTime(0, 0)) + end.setTime(QTime(23, 59)) + + # Load from existing windows if present + key = str(wd) + day_windows = self._windows.get(key) + if isinstance(day_windows, list) and len(day_windows) > 0 and isinstance(day_windows[0], dict): + w0 = day_windows[0] + s = str(w0.get("start", "00:00")) + e = str(w0.get("end", "23:59")) + day_enabled.setChecked(True) + start.setTime(self._parse_qtime(s, QTime(0, 0))) + end.setTime(self._parse_qtime(e, QTime(23, 59))) + else: + day_enabled.setChecked(False) + + # Disable edits when not enabled + def _apply_enabled_state(_=None, *, _day_enabled=day_enabled, _start=start, _end=end): + en = _day_enabled.isChecked() + _start.setEnabled(en) + _end.setEnabled(en) + + day_enabled.stateChanged.connect(_apply_enabled_state) + _apply_enabled_state() + + row.addWidget(day_enabled, 1) + row.addWidget(QLabel("Start")) + row.addWidget(start) + row.addWidget(QLabel("End")) + row.addWidget(end) + + win_lay.addLayout(row) + + self._day_controls[wd] = {"enabled": day_enabled, "start": start, "end": end} + + layout.addWidget(win_frame) + btn_row = QHBoxLayout() btn_row.addStretch(1) - cancel_btn = QPushButton("Cancel") ok_btn = QPushButton("Save") cancel_btn.clicked.connect(self.reject) ok_btn.clicked.connect(self._on_ok) - btn_row.addWidget(cancel_btn) btn_row.addWidget(ok_btn) layout.addLayout(btn_row) + self._sync_enabled_state() + + self.enabled.stateChanged.connect(lambda _i: self._sync_enabled_state()) + + def _sync_enabled_state(self): + en = self.enabled.isChecked() + self.every_minutes.setEnabled(en) + for wd, ctrls in self._day_controls.items(): + ctrls["enabled"].setEnabled(en) + # if schedule disabled, visually disable time edits too + if not en: + ctrls["start"].setEnabled(False) + ctrls["end"].setEnabled(False) + else: + # restore per-day checkbox logic + day_en = ctrls["enabled"].isChecked() + ctrls["start"].setEnabled(day_en) + ctrls["end"].setEnabled(day_en) + + def _parse_qtime(self, s: str, default: QTime) -> QTime: + try: + parts = s.strip().split(":") + if len(parts) != 2: + return default + hh = int(parts[0]) + mm = int(parts[1]) + if hh < 0 or hh > 23 or mm < 0 or mm > 59: + return default + return QTime(hh, mm) + except Exception: + return default + def _on_ok(self): if self.enabled.isChecked() and self.every_minutes.value() < 1: MsgBox.show(self, "Schedule", "Every minutes must be at least 1 if enabled.", icon="warning") return + + # Basic validation: ensure enabled days have valid times (QTimeEdit ensures format) + # Allow overnight windows naturally (start > end) — that's intended. self.accept() def value(self) -> Dict[str, Any]: + windows: Dict[str, Any] = {} + if self.enabled.isChecked(): + for wd, ctrls in self._day_controls.items(): + if not ctrls["enabled"].isChecked(): + continue + s = ctrls["start"].time().toString("HH:mm") + e = ctrls["end"].time().toString("HH:mm") + windows[str(wd)] = [{"start": s, "end": e}] + return { "enabled": bool(self.enabled.isChecked()), "everyMinutes": int(self.every_minutes.value()), + "windows": windows, } From d7832aa5f1c78f15dcad5ff911cda522efbd6b4d Mon Sep 17 00:00:00 2001 From: Louis Ouellet Date: Thu, 15 Jan 2026 13:19:21 -0500 Subject: [PATCH 24/51] General: Appending the namespace --- src/replicator/__init__.py | 5 +++-- src/replicator/replicator.py | 2 -- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/replicator/__init__.py b/src/replicator/__init__.py index 7748ab7..edab12c 100644 --- a/src/replicator/__init__.py +++ b/src/replicator/__init__.py @@ -3,8 +3,9 @@ from .replicator import Replicator from .ui import JobDialog, ScheduleDialog -from .job import Job, Schedule, Endpoint +from .job import Schedule, Endpoint, Job, JobRunResult, JobStore +from .migration import Migration __version__ = "1.0.0" -__all__ = ["Replicator", "JobDialog", "ScheduleDialog"] +__all__ = ["Replicator", "JobDialog", "ScheduleDialog", "Schedule", "Endpoint", "Job", "JobRunResult", "JobStore", "Migration"] diff --git a/src/replicator/replicator.py b/src/replicator/replicator.py index cb6a1cd..4237647 100644 --- a/src/replicator/replicator.py +++ b/src/replicator/replicator.py @@ -523,8 +523,6 @@ def __init__( self._fs = FileSystem(helper=self._helper, logger=self._logger) - # Legacy configuration jobs bootstrap removed. - # --- Database path setup --- # Use Helper.get_cwd() if present, else os.getcwd() if hasattr(self._helper, "get_cwd") and callable(getattr(self._helper, "get_cwd", None)): From 755a05c3cc6cd04a2d9ab685d7aea7674d0e7b50 Mon Sep 17 00:00:00 2001 From: Louis Ouellet Date: Thu, 15 Jan 2026 14:16:48 -0500 Subject: [PATCH 25/51] BUGFIX: Addressing various issues with the scheduling schema and process. --- src/replicator/job.py | 165 ++++++++++++++++++++++++++++++----- src/replicator/migration.py | 35 +++++++- src/replicator/replicator.py | 103 ++++++++++++++++++++-- src/replicator/ui.py | 110 +++++++++++++++++++---- 4 files changed, 364 insertions(+), 49 deletions(-) diff --git a/src/replicator/job.py b/src/replicator/job.py index 840ffb8..344b740 100644 --- a/src/replicator/job.py +++ b/src/replicator/job.py @@ -137,21 +137,58 @@ def from_db_row(row: Mapping[str, Any]) -> "Endpoint": @dataclass(frozen=True) class Schedule: - enabled: bool = False + """Job schedule. + + New model: + - intervalSeconds: global default interval + - windows: dict weekday -> list[window] + where window = {"start":"HH:mm","end":"HH:mm","intervalSeconds":int(optional)} + + Backward compatibility: + - everyMinutes is kept for legacy callers / older DB rows. + - If intervalSeconds is missing, it is derived from everyMinutes * 60. + """ + + enabled: bool = True + + # New + intervalSeconds: int = 3600 + + # Legacy everyMinutes: int = 60 - windows: JsonDict = field(default_factory=dict) # persisted as JSON in schedule.windows + + # persisted as JSON in schedule.windows + windows: JsonDict = field(default_factory=dict) + nextRunAt: Optional[str] = None lastScheduledRunAt: Optional[str] = None def validate(self) -> List[str]: errs: List[str] = [] if self.enabled: - if self.everyMinutes <= 0: - errs.append("Schedule everyMinutes must be > 0 when schedule is enabled.") + if int(self.interval_seconds()) <= 0: + errs.append("Schedule intervalSeconds must be > 0 when schedule is enabled.") if self.windows and not isinstance(self.windows, dict): errs.append("Schedule windows must be a dict of weekday -> list[window].") return errs + def interval_seconds(self) -> int: + """Return global interval in seconds (always >= 1 when enabled).""" + try: + s = int(self.intervalSeconds or 0) + except Exception: + s = 0 + if s > 0: + return s + # fallback from legacy minutes + try: + m = int(self.everyMinutes or 0) + except Exception: + m = 0 + if m <= 0: + return 1 + return max(1, m * 60) + def _parse_hhmm(self, s: str) -> Optional[time]: try: parts = s.strip().split(":") @@ -165,20 +202,51 @@ def _parse_hhmm(self, s: str) -> Optional[time]: except Exception: return None + def _day_windows(self, weekday: int) -> List[Dict[str, Any]]: + if not self.windows: + return [] + dw = self.windows.get(str(weekday)) + if dw is None: + dw = self.windows.get(weekday) + if not isinstance(dw, list): + return [] + out: List[Dict[str, Any]] = [] + for w in dw: + if isinstance(w, dict): + out.append(w) + return out + + def _interval_for_day(self, weekday: int) -> int: + """Return the intervalSeconds for a weekday. + + If windows are configured for that weekday and the first window includes intervalSeconds, use it. + Otherwise fall back to global intervalSeconds. + """ + day_ws = self._day_windows(weekday) + if day_ws: + w0 = day_ws[0] + if "intervalSeconds" in w0: + try: + v = int(w0.get("intervalSeconds") or 0) + if v > 0: + return v + except Exception: + pass + return self.interval_seconds() + def _in_window_for_day(self, dt: datetime) -> bool: + # No windows configured => always allowed. if not self.windows: return True wd = dt.weekday() # 0..6 - day_windows = self.windows.get(str(wd)) or self.windows.get(wd) + day_windows = self._day_windows(wd) if not day_windows: return False tnow = dt.timetz().replace(tzinfo=None) for w in day_windows: - if not isinstance(w, dict): - continue s = w.get("start") e = w.get("end") if not s or not e: @@ -205,24 +273,31 @@ def should_run_now(self, now: Optional[datetime] = None) -> bool: return self._in_window_for_day(now) def next_run_at(self, now: Optional[datetime] = None) -> Optional[datetime]: + """Compute the next aligned run time within allowed windows. + + Alignment is based on Unix epoch seconds modulo the intervalSeconds for that day. + Searches up to 7 days ahead. + """ if not self.enabled: return None - if self.everyMinutes <= 0: - return now or datetime.now(timezone.utc) now = now or datetime.now(timezone.utc) - cur = now.replace(second=0, microsecond=0) + timedelta(minutes=1) - - def aligned(dt: datetime) -> bool: - epoch = datetime(1970, 1, 1, tzinfo=timezone.utc) - minutes = int((dt - epoch).total_seconds() // 60) - return (minutes % int(self.everyMinutes)) == 0 + # Start looking from next second (normalized) + cur = now.replace(microsecond=0) + timedelta(seconds=1) limit = cur + timedelta(days=7) while cur <= limit: - if aligned(cur) and self._in_window_for_day(cur): - return cur - cur += timedelta(minutes=1) + if self._in_window_for_day(cur): + interval = self._interval_for_day(cur.weekday()) + if interval <= 0: + interval = 1 + try: + epoch_sec = int(cur.timestamp()) + except Exception: + epoch_sec = 0 + if (epoch_sec % int(interval)) == 0: + return cur + cur += timedelta(seconds=1) return None @@ -402,9 +477,22 @@ def to_row_dicts(self) -> Dict[str, Any]: self.targetEndpoint.to_db_fields("target"), ] + # Persist the schedule windows as JSON; include per-day intervalSeconds inside windows. + # Keep everyMinutes for backward compatibility (best-effort conversion). + try: + global_interval = int(self.schedule.interval_seconds()) + except Exception: + # Fall back to the schedule's declared defaults rather than a magic number + try: + global_interval = int(getattr(self.schedule, "intervalSeconds", 3600) or 3600) + except Exception: + global_interval = 3600 + + compat_minutes = max(1, int(round(float(global_interval) / 60.0))) + sched_row = { "enabled": 1 if self.schedule.enabled else 0, - "everyMinutes": int(self.schedule.everyMinutes), + "everyMinutes": int(getattr(self.schedule, "everyMinutes", compat_minutes) or compat_minutes), "nextRunAt": self.schedule.nextRunAt, "lastScheduledRunAt": self.schedule.lastScheduledRunAt, "windows": json.dumps(self.schedule.windows or {}) if self.schedule.windows else None, @@ -461,15 +549,47 @@ def from_db_rows( except Exception: windows = {} + # Backward compatible interval handling: + # - Prefer intervalSeconds embedded in windows (per-day) + # - Else derive from everyMinutes + derived_seconds = None + try: + # Try the first configured window's intervalSeconds + for _k, _v in (windows or {}).items(): + if isinstance(_v, list) and _v and isinstance(_v[0], dict) and "intervalSeconds" in _v[0]: + iv = int(_v[0].get("intervalSeconds") or 0) + if iv > 0: + derived_seconds = iv + break + except Exception: + derived_seconds = None + + if derived_seconds is None: + try: + derived_seconds = int(schedule_row.get("everyMinutes", 60) or 60) * 60 + except Exception: + # Default to the Schedule dataclass default (intervalSeconds) rather than a scattered magic number + derived_seconds = int(getattr(Schedule, "__dataclass_fields__", {}).get("intervalSeconds").default) if hasattr(Schedule, "__dataclass_fields__") else 3600 + + try: + derived_seconds = int(derived_seconds or 0) + except Exception: + derived_seconds = 0 + if derived_seconds <= 0: + # fall back to Schedule default intervalSeconds + derived_seconds = int(getattr(Schedule, "__dataclass_fields__", {}).get("intervalSeconds").default) if hasattr(Schedule, "__dataclass_fields__") else 3600 + j.schedule = Schedule( - enabled=bool(schedule_row.get("enabled", 0)), + enabled=bool(schedule_row.get("enabled", 1)), + intervalSeconds=int(derived_seconds), everyMinutes=int(schedule_row.get("everyMinutes", 60) or 60), windows=windows, nextRunAt=schedule_row.get("nextRunAt"), lastScheduledRunAt=schedule_row.get("lastScheduledRunAt"), ) else: - j.schedule = Schedule(enabled=False, everyMinutes=60, windows={}) + # Use Schedule dataclass defaults (enabled/intervalSeconds/everyMinutes) when no DB row exists. + j.schedule = Schedule(windows={}) return j @@ -488,6 +608,7 @@ def to_legacy_dict(self) -> JsonDict: "targetEndpoint": {"type": self.targetEndpoint.type, "location": self.targetEndpoint.location, "auth": dict(self.targetEndpoint.auth)}, "schedule": { "enabled": self.schedule.enabled, + "intervalSeconds": self.schedule.interval_seconds(), "everyMinutes": self.schedule.everyMinutes, "windows": self.schedule.windows, }, @@ -688,7 +809,7 @@ def upsert(self, job: Job) -> int: s_data = { "jobId": job_id, "enabled": int(schedule_row.get("enabled") or 0), - "everyMinutes": int(schedule_row.get("everyMinutes") or 60), + "everyMinutes": int(schedule_row.get("everyMinutes") or 1), "nextRunAt": schedule_row.get("nextRunAt"), "lastScheduledRunAt": schedule_row.get("lastScheduledRunAt"), "windows": schedule_row.get("windows"), diff --git a/src/replicator/migration.py b/src/replicator/migration.py index 4986972..2259c7b 100644 --- a/src/replicator/migration.py +++ b/src/replicator/migration.py @@ -101,7 +101,14 @@ def _apply_migrations(self, migrations: Sequence[Tuple[str, Sequence[str]]]) -> try: with self._db.transaction(): for stmt in stmts: - self._db.execute(stmt) + try: + self._db.execute(stmt) + except Exception as e: + # SQLite: ignore duplicate column errors when applying additive migrations on fresh schemas + msg = str(e).lower() + if "duplicate column name" in msg: + continue + raise self._db.execute("INSERT INTO schema_migrations (name) VALUES (?)", (name,)) self._log(f"[Migration] applied {name}", level="debug") @@ -163,10 +170,14 @@ def _migrations(self) -> List[Tuple[str, List[str]]]: created DATETIME DEFAULT CURRENT_TIMESTAMP, modified DATETIME DEFAULT CURRENT_TIMESTAMP, jobId INTEGER NOT NULL UNIQUE, - enabled INTEGER NOT NULL DEFAULT 0, + -- Default schedule: enabled, all day/every day via empty windows (interpreted as always allowed), + -- intervalSeconds=3600 (1h). Keep everyMinutes for backward compatibility. + enabled INTEGER NOT NULL DEFAULT 1, + intervalSeconds INTEGER NOT NULL DEFAULT 3600, everyMinutes INTEGER NOT NULL DEFAULT 60, nextRunAt TEXT NULL, lastScheduledRunAt TEXT NULL, + windows TEXT NULL, FOREIGN KEY(jobId) REFERENCES jobs(id) ON DELETE CASCADE ); """, @@ -257,9 +268,25 @@ def _migrations(self) -> List[Tuple[str, List[str]]]: ( "0004_schedule_windows", [ - # Store schedule windows as JSON (dict weekday -> list[{start,end},...]) - # This is the minimal schema required to support your UI scheduling window editor. + # Add windows if missing (SQLite has no IF NOT EXISTS for ADD COLUMN; ignore error if it already exists) + """ + SELECT 1; + """, + """ + -- handled in code: Migration will attempt the ALTER; if it fails due to duplicate column, it should be ignored. + """, "ALTER TABLE schedule ADD COLUMN windows TEXT NULL;", ], ), + ( + "0005_schedule_interval_seconds", + [ + # Add global interval in seconds (new schedule model). Keep legacy everyMinutes for compatibility. + "ALTER TABLE schedule ADD COLUMN intervalSeconds INTEGER NOT NULL DEFAULT 3600;", + # Backfill intervalSeconds from everyMinutes where possible (existing rows). + "UPDATE schedule SET intervalSeconds = CASE WHEN intervalSeconds IS NULL OR intervalSeconds <= 0 THEN (everyMinutes * 60) ELSE intervalSeconds END;", + # Ensure enabled defaults to on for existing rows that still have the legacy default off (best-effort). + "UPDATE schedule SET enabled = 1 WHERE enabled IS NULL;", + ], + ), ] diff --git a/src/replicator/replicator.py b/src/replicator/replicator.py index 4237647..8d8a724 100644 --- a/src/replicator/replicator.py +++ b/src/replicator/replicator.py @@ -716,10 +716,88 @@ def _it(v: Any) -> QTableWidgetItem: self._table.setItem(r, 5, _it(job.lastResult or "")) + def _default_interval_seconds(self) -> int: + """Return the configured default interval in seconds (service.defaultInterval), fallback to 3600.""" + try: + v = self._configuration.get("service.defaultInterval", 3600) + iv = int(v) + return iv if iv > 0 else 3600 + except Exception: + return 3600 + + def _default_schedule_dict(self) -> Dict[str, Any]: + interval = self._default_interval_seconds() + windows: Dict[str, Any] = {} + for wd in range(7): + windows[str(wd)] = [{"start": "00:00", "end": "23:59", "intervalSeconds": interval}] + return { + "enabled": True, + "intervalSeconds": interval, + "windows": windows, + } + + def _schedule_interval_seconds_for_now(self, sched: Dict[str, Any]) -> int: + """Best-effort interval seconds for logging (supports per-day interval stored in window dicts).""" + try: + windows = sched.get("windows") if isinstance(sched.get("windows"), dict) else {} + wd = datetime.now(timezone.utc).weekday() + day = windows.get(str(wd)) or windows.get(wd) # type: ignore[index] + if isinstance(day, list) and day and isinstance(day[0], dict): + v = day[0].get("intervalSeconds") + if v is not None: + return int(v) + except Exception: + pass + + default_interval = self._default_interval_seconds() + + try: + if "intervalSeconds" in sched: + v = int(sched.get("intervalSeconds") or 0) + return v if v > 0 else default_interval + except Exception: + pass + try: + v = int(sched.get("everyMinutes", 60) or 60) * 60 + return v if v > 0 else default_interval + except Exception: + return default_interval + def _job_from_legacy_dict(self, d: Dict[str, Any], *, existing_id: Optional[int] = None) -> Job: src = d.get("sourceEndpoint") or {"type": "local", "location": d.get("source", ""), "auth": {}} tgt = d.get("targetEndpoint") or {"type": "local", "location": d.get("target", ""), "auth": {}} - sched = d.get("schedule") or {} + + # Schedule defaults: + # - enabled + # - every day, all day + # - interval 3600 seconds (1h) + sched = d.get("schedule") + if not isinstance(sched, dict): + sched = self._default_schedule_dict() + + default_interval = self._default_interval_seconds() + + # Backward compatibility: accept older key `everyMinutes` + if "intervalSeconds" not in sched: + try: + v = int(sched.get("everyMinutes", 60) or 60) * 60 + sched["intervalSeconds"] = v if v > 0 else default_interval + except Exception: + sched["intervalSeconds"] = default_interval + + # Ensure windows exist; if missing, make it "every day, all day" with per-day interval + windows = sched.get("windows") + if not isinstance(windows, dict) or not windows: + windows = {} + for wd in range(7): + windows[str(wd)] = [{"start": "00:00", "end": "23:59", "intervalSeconds": int(sched.get("intervalSeconds") or default_interval)}] + sched["windows"] = windows + else: + # Ensure each enabled day window includes intervalSeconds (per-day) for the upcoming scheduler logic + for k, v in list(windows.items()): + if isinstance(v, list) and v and isinstance(v[0], dict): + if "intervalSeconds" not in v[0]: + v[0]["intervalSeconds"] = int(sched.get("intervalSeconds") or default_interval) j = Job( id=existing_id, @@ -748,9 +826,19 @@ def _job_from_legacy_dict(self, d: Dict[str, Any], *, existing_id: Optional[int] ) windows = sched.get("windows") if isinstance(sched.get("windows"), dict) else {} + + # NOTE: The domain model currently uses `everyMinutes`. + # For now, we map intervalSeconds -> everyMinutes (rounded down, minimum 1) for compatibility. + interval_seconds = default_interval + try: + interval_seconds = int(sched.get("intervalSeconds") or default_interval) + except Exception: + interval_seconds = default_interval + every_minutes = max(1, int(interval_seconds // 60)) + j.schedule = Schedule( - enabled=bool(sched.get("enabled", False)), - everyMinutes=int(sched.get("everyMinutes", 60) or 60), + enabled=bool(sched.get("enabled", True)), + everyMinutes=every_minutes, windows=windows, ) @@ -760,6 +848,8 @@ def _add_job(self): dlg = JobDialog(self) if dlg.exec_() == QDialog.Accepted: new_job_dict = dlg.value() + if "schedule" not in new_job_dict or not isinstance(new_job_dict.get("schedule"), dict): + new_job_dict["schedule"] = self._default_schedule_dict() job_obj = self._job_from_legacy_dict(new_job_dict) job_id = self._store.upsert(job_obj) job_obj.id = job_id @@ -907,15 +997,16 @@ def _run_job(self, job: Job) -> bool: preserve_metadata = bool(job.preserveMetadata) mode = job.mode direction = job.direction - schedule = job.schedule.__dict__ if job.schedule else {} + schedule = job.to_legacy_dict().get("schedule") or {} if not src or not dst: self._log(f"[Replicator] Job '{name}' invalid: missing source/target.", level="error") return False sched_str = "" - if getattr(job.schedule, "enabled", False): - sched_str = f", schedule=Every {getattr(job.schedule, 'everyMinutes', 60)} min" + if isinstance(schedule, dict) and bool(schedule.get("enabled", True)): + interval_s = self._schedule_interval_seconds_for_now(schedule) + sched_str = f", schedule=Interval {interval_s}s" logline = f"[Replicator] Running job '{name}': {src} -> {dst} (mode={mode}, direction={direction}, delete={allow_deletion}, meta={preserve_metadata}{sched_str}" logline += f", srcType={src_type}, dstType={dst_type})" self._log(logline) diff --git a/src/replicator/ui.py b/src/replicator/ui.py index 3151c11..caa0835 100644 --- a/src/replicator/ui.py +++ b/src/replicator/ui.py @@ -35,7 +35,7 @@ # Job Dialog # ------------------------------------------------------------------ class JobDialog(QDialog): - def __init__(self, parent=None, job: Optional[Dict[str, Any]] = None): + def __init__(self, parent=None, job: Optional[Dict[str, Any]] = None, *, default_interval_seconds: Optional[int] = None): super().__init__(parent) self.setWindowTitle("Replication Job") self.setModal(True) @@ -46,6 +46,15 @@ def __init__(self, parent=None, job: Optional[Dict[str, Any]] = None): job = job or {} self._original_job = job + # Default interval for new schedules (seconds). Provided by the app (e.g. Configuration service.defaultInterval). + try: + di = int(default_interval_seconds) if default_interval_seconds is not None else 3600 + except Exception: + di = 3600 + if di <= 0: + di = 3600 + self._default_interval_seconds = di + layout = QVBoxLayout(self) layout.setAlignment(Qt.AlignTop) layout.setSizeConstraint(QLayout.SetMinimumSize) @@ -249,7 +258,7 @@ def _get_endpoint(key_endpoint: str, key_str: str, default_type: str = "local") def _open_schedule(self) -> None: job = self._original_job if isinstance(self._original_job, dict) else {} - dlg = ScheduleDialog(self, job=job) + dlg = ScheduleDialog(self, job=job, default_interval_seconds=self._default_interval_seconds) if dlg.exec_() == QDialog.Accepted: # store schedule back into original job dict so value() preserves it if not isinstance(self._original_job, dict): @@ -615,8 +624,11 @@ class ScheduleDialog(QDialog): Persists as: schedule = { "enabled": bool, - "everyMinutes": int, - "windows": { "0":[{"start":"22:00","end":"06:00"}], ... } + "intervalSeconds": int, # global default/fallback + "windows": { + "0":[{"start":"22:00","end":"06:00","intervalSeconds":3600}], + ... + } } """ @@ -630,13 +642,47 @@ class ScheduleDialog(QDialog): (6, "Sunday"), ] - def __init__(self, parent=None, job: Optional[Dict[str, Any]] = None): + def __init__(self, parent=None, job: Optional[Dict[str, Any]] = None, *, default_interval_seconds: Optional[int] = None): super().__init__(parent) self.setWindowTitle("Schedule") self.setModal(True) + # Default interval for new schedules (seconds). Provided by the app (e.g. Configuration service.defaultInterval). + try: + di = int(default_interval_seconds) if default_interval_seconds is not None else 3600 + except Exception: + di = 3600 + if di <= 0: + di = 3600 + self._default_interval_seconds = di + job = job or {} - schedule = job.get("schedule", {}) if isinstance(job.get("schedule", {}), dict) else {} + schedule = job.get("schedule") if isinstance(job.get("schedule"), dict) else {} + if not schedule: + # Default schedule: enabled, every day, all day, default interval + schedule = {"enabled": True, "intervalSeconds": int(self._default_interval_seconds), "windows": {}} + for wd in range(7): + schedule["windows"][str(wd)] = [{"start": "00:00", "end": "23:59", "intervalSeconds": int(self._default_interval_seconds)}] + else: + # Backward compatibility: everyMinutes -> intervalSeconds + if "intervalSeconds" not in schedule: + try: + schedule["intervalSeconds"] = int(schedule.get("everyMinutes", 60) or 60) * 60 + except Exception: + schedule["intervalSeconds"] = int(self._default_interval_seconds) + # Ensure windows exists; if missing, default to every day all day + if not isinstance(schedule.get("windows"), dict) or not schedule.get("windows"): + schedule["windows"] = {} + for wd in range(7): + schedule["windows"][str(wd)] = [{"start": "00:00", "end": "23:59", "intervalSeconds": int(schedule.get("intervalSeconds") or self._default_interval_seconds)}] + else: + # Ensure each day window has intervalSeconds + try: + for _k, _v in list(schedule["windows"].items()): + if isinstance(_v, list) and _v and isinstance(_v[0], dict) and "intervalSeconds" not in _v[0]: + _v[0]["intervalSeconds"] = int(schedule.get("intervalSeconds") or self._default_interval_seconds) + except Exception: + pass self._windows: Dict[str, Any] = schedule.get("windows", {}) if isinstance(schedule.get("windows", {}), dict) else {} @@ -650,12 +696,12 @@ def __init__(self, parent=None, job: Optional[Dict[str, Any]] = None): self.enabled = QCheckBox() self.enabled.setChecked(bool(schedule.get("enabled", False))) - self.every_minutes = QSpinBox() - self.every_minutes.setRange(1, 10080) - self.every_minutes.setValue(int(schedule.get("everyMinutes", 60))) + self.interval_seconds = QSpinBox() + self.interval_seconds.setRange(1, 604800) # 1s .. 7 days + self.interval_seconds.setValue(int(schedule.get("intervalSeconds", self._default_interval_seconds) or self._default_interval_seconds)) form.addRow("Enabled", self.enabled) - form.addRow("Every minutes", self.every_minutes) + form.addRow("Interval (in seconds)", self.interval_seconds) # Windows editor win_frame = QFrame() @@ -682,6 +728,11 @@ def __init__(self, parent=None, job: Optional[Dict[str, Any]] = None): start.setTime(QTime(0, 0)) end.setTime(QTime(23, 59)) + interval = QSpinBox() + interval.setRange(1, 604800) # 1s .. 7 days + interval.setFixedWidth(130) + interval.setValue(int(schedule.get("intervalSeconds", self._default_interval_seconds) or self._default_interval_seconds)) + # Load from existing windows if present key = str(wd) day_windows = self._windows.get(key) @@ -692,14 +743,23 @@ def __init__(self, parent=None, job: Optional[Dict[str, Any]] = None): day_enabled.setChecked(True) start.setTime(self._parse_qtime(s, QTime(0, 0))) end.setTime(self._parse_qtime(e, QTime(23, 59))) + try: + interval.setValue(int(w0.get("intervalSeconds", schedule.get("intervalSeconds", self._default_interval_seconds)) or self._default_interval_seconds)) + except Exception: + interval.setValue(int(schedule.get("intervalSeconds", self._default_interval_seconds) or self._default_interval_seconds)) else: day_enabled.setChecked(False) + try: + interval.setValue(int(schedule.get("intervalSeconds", self._default_interval_seconds) or self._default_interval_seconds)) + except Exception: + interval.setValue(int(self._default_interval_seconds)) # Disable edits when not enabled - def _apply_enabled_state(_=None, *, _day_enabled=day_enabled, _start=start, _end=end): + def _apply_enabled_state(_=None, *, _day_enabled=day_enabled, _start=start, _end=end, _interval=interval): en = _day_enabled.isChecked() _start.setEnabled(en) _end.setEnabled(en) + _interval.setEnabled(en) day_enabled.stateChanged.connect(_apply_enabled_state) _apply_enabled_state() @@ -709,10 +769,12 @@ def _apply_enabled_state(_=None, *, _day_enabled=day_enabled, _start=start, _end row.addWidget(start) row.addWidget(QLabel("End")) row.addWidget(end) + row.addWidget(QLabel("Interval (s)")) + row.addWidget(interval) win_lay.addLayout(row) - self._day_controls[wd] = {"enabled": day_enabled, "start": start, "end": end} + self._day_controls[wd] = {"enabled": day_enabled, "start": start, "end": end, "interval": interval} layout.addWidget(win_frame) @@ -732,18 +794,20 @@ def _apply_enabled_state(_=None, *, _day_enabled=day_enabled, _start=start, _end def _sync_enabled_state(self): en = self.enabled.isChecked() - self.every_minutes.setEnabled(en) + self.interval_seconds.setEnabled(en) for wd, ctrls in self._day_controls.items(): ctrls["enabled"].setEnabled(en) # if schedule disabled, visually disable time edits too if not en: ctrls["start"].setEnabled(False) ctrls["end"].setEnabled(False) + ctrls["interval"].setEnabled(False) else: # restore per-day checkbox logic day_en = ctrls["enabled"].isChecked() ctrls["start"].setEnabled(day_en) ctrls["end"].setEnabled(day_en) + ctrls["interval"].setEnabled(day_en) def _parse_qtime(self, s: str, default: QTime) -> QTime: try: @@ -759,26 +823,38 @@ def _parse_qtime(self, s: str, default: QTime) -> QTime: return default def _on_ok(self): - if self.enabled.isChecked() and self.every_minutes.value() < 1: - MsgBox.show(self, "Schedule", "Every minutes must be at least 1 if enabled.", icon="warning") + if self.enabled.isChecked() and self.interval_seconds.value() < 1: + MsgBox.show(self, "Schedule", "Interval (in seconds) must be at least 1 if enabled.", icon="warning") return + if self.enabled.isChecked(): + for _wd, ctrls in self._day_controls.items(): + if not ctrls["enabled"].isChecked(): + continue + if int(ctrls["interval"].value() or 0) < 1: + MsgBox.show(self, "Schedule", "Per-day interval must be at least 1 second for enabled days.", icon="warning") + return + # Basic validation: ensure enabled days have valid times (QTimeEdit ensures format) # Allow overnight windows naturally (start > end) — that's intended. self.accept() def value(self) -> Dict[str, Any]: windows: Dict[str, Any] = {} + if self.enabled.isChecked(): for wd, ctrls in self._day_controls.items(): if not ctrls["enabled"].isChecked(): continue s = ctrls["start"].time().toString("HH:mm") e = ctrls["end"].time().toString("HH:mm") - windows[str(wd)] = [{"start": s, "end": e}] + itv = int(ctrls["interval"].value()) + windows[str(wd)] = [{"start": s, "end": e, "intervalSeconds": itv}] return { "enabled": bool(self.enabled.isChecked()), - "everyMinutes": int(self.every_minutes.value()), + "intervalSeconds": int(self.interval_seconds.value()), + # Backward compatibility (old key) for any older code paths: + "everyMinutes": max(1, int(int(self.interval_seconds.value()) // 60)), "windows": windows, } From 4627f73cdd5511b3777e22cb6caab37f1c40bbc8 Mon Sep 17 00:00:00 2001 From: Louis Ouellet Date: Thu, 15 Jan 2026 14:44:36 -0500 Subject: [PATCH 26/51] BUGFIX: Fixing the default schedule intervals. --- src/replicator/replicator.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/replicator/replicator.py b/src/replicator/replicator.py index 8d8a724..7ae40f7 100644 --- a/src/replicator/replicator.py +++ b/src/replicator/replicator.py @@ -845,7 +845,8 @@ def _job_from_legacy_dict(self, d: Dict[str, Any], *, existing_id: Optional[int] return j def _add_job(self): - dlg = JobDialog(self) + # Pass configured service.defaultInterval into the dialog so Schedule defaults match user config + dlg = JobDialog(self, default_interval_seconds=self._default_interval_seconds()) if dlg.exec_() == QDialog.Accepted: new_job_dict = dlg.value() if "schedule" not in new_job_dict or not isinstance(new_job_dict.get("schedule"), dict): @@ -862,7 +863,8 @@ def _edit_job(self): return current = self._jobs[idx] - dlg = JobDialog(self, current.to_legacy_dict()) + # Pass configured service.defaultInterval into the dialog so Schedule defaults remain consistent + dlg = JobDialog(self, current.to_legacy_dict(), default_interval_seconds=self._default_interval_seconds()) if dlg.exec_() == QDialog.Accepted: updated_dict = dlg.value() job_obj = self._job_from_legacy_dict(updated_dict, existing_id=current.id) @@ -915,7 +917,8 @@ def _edit_schedule(self): return current = self._jobs[idx] - dlg = ScheduleDialog(self, job=current.to_legacy_dict()) + # Pass configured service.defaultInterval into the schedule editor so defaults match user config + dlg = ScheduleDialog(self, job=current.to_legacy_dict(), default_interval_seconds=self._default_interval_seconds()) if dlg.exec_() == QDialog.Accepted: d = current.to_legacy_dict() d["schedule"] = dlg.value() From 3e09207af37f624ae6e4136addb775186b3d7cf6 Mon Sep 17 00:00:00 2001 From: Louis Ouellet Date: Thu, 15 Jan 2026 14:45:01 -0500 Subject: [PATCH 27/51] BUGFIX: Fixing the path fields so they open the browse window. --- src/replicator/ui.py | 47 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/src/replicator/ui.py b/src/replicator/ui.py index caa0835..e600e74 100644 --- a/src/replicator/ui.py +++ b/src/replicator/ui.py @@ -5,7 +5,7 @@ from typing import Optional, Any, Dict, List, Tuple -from PyQt5.QtCore import Qt, QTime +from PyQt5.QtCore import Qt, QTime, pyqtSignal from PyQt5.QtWidgets import ( QWidget, QVBoxLayout, @@ -23,6 +23,7 @@ QSizePolicy, QLayout, QTimeEdit, + QFileDialog, ) try: @@ -31,6 +32,21 @@ from ui import MsgBox +# ------------------------------------------------------------------ +# Helpers +# ------------------------------------------------------------------ +class BrowseLineEdit(QLineEdit): + """QLineEdit that emits a signal when clicked (used to open browse dialogs).""" + clicked = pyqtSignal() + + def mousePressEvent(self, event): + try: + self.clicked.emit() + except Exception: + pass + super().mousePressEvent(event) + + # ------------------------------------------------------------------ # Job Dialog # ------------------------------------------------------------------ @@ -279,7 +295,7 @@ def _build_endpoint(self, existing: Dict[str, Any]): type_combo.setProperty("existing_type", existing.get("type", "local")) type_combo.setProperty("existing_auth", existing.get("auth", {}) or {}) - location_edit = QLineEdit(existing.get("location", "")) + location_edit = BrowseLineEdit(existing.get("location", "")) port_spin = QSpinBox() port_spin.setRange(1, 65535) @@ -302,6 +318,29 @@ def _build_endpoint(self, existing: Dict[str, Any]): fields_lay.addWidget(location_edit, 1) fields_lay.addWidget(port_spin) + def _browse_location(): + try: + typ = (type_combo.currentText() or "").lower() + if typ != "local": + return + start_dir = location_edit.text().strip() or "" + # If the user typed a file path, prefer its directory. + if start_dir and not start_dir.endswith("/") and not start_dir.endswith("\\"): + try: + import os + if os.path.isfile(start_dir): + start_dir = os.path.dirname(start_dir) + except Exception: + pass + selected = QFileDialog.getExistingDirectory(self, "Select folder", start_dir) + if selected: + location_edit.setText(selected) + except Exception: + return + + # Click-to-browse for local paths (no extra button needed). + location_edit.clicked.connect(_browse_location) + endpoint_row = QWidget() endpoint_row.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) endpoint_form = QFormLayout(endpoint_row) @@ -454,6 +493,10 @@ def _on_type_changed( "ssh": "SSH host/path (e.g. example.com:/folder)", }.get(typ, "") location_edit.setPlaceholderText(placeholder) + # For local endpoints, clicking the location field opens a folder chooser. + # For non-local endpoints, keep normal typing behavior. + if (typ or "").lower() == "local": + location_edit.setCursorPosition(len(location_edit.text())) if typ in ("ftp", "ssh"): port_spin.setVisible(True) From 7d736ba303472c229575558c25af0268cf05567b Mon Sep 17 00:00:00 2001 From: Louis Ouellet Date: Fri, 16 Jan 2026 10:30:45 -0500 Subject: [PATCH 28/51] BUGFIX: Fixing the scheduling --- src/replicator/job.py | 10 +- src/replicator/replicator.py | 220 ++++++++++++++++++++++++++++++++++- test.sh | 3 + 3 files changed, 228 insertions(+), 5 deletions(-) create mode 100755 test.sh diff --git a/src/replicator/job.py b/src/replicator/job.py index 344b740..47b6d48 100644 --- a/src/replicator/job.py +++ b/src/replicator/job.py @@ -269,7 +269,8 @@ def _in_window_for_day(self, dt: datetime) -> bool: def should_run_now(self, now: Optional[datetime] = None) -> bool: if not self.enabled: return False - now = now or datetime.now(timezone.utc) + # Windows are defined in local (wall-clock) time. + now = now or datetime.now().astimezone() return self._in_window_for_day(now) def next_run_at(self, now: Optional[datetime] = None) -> Optional[datetime]: @@ -281,7 +282,8 @@ def next_run_at(self, now: Optional[datetime] = None) -> Optional[datetime]: if not self.enabled: return None - now = now or datetime.now(timezone.utc) + # Windows are defined in local (wall-clock) time. + now = now or datetime.now().astimezone() # Start looking from next second (normalized) cur = now.replace(microsecond=0) + timedelta(seconds=1) @@ -493,6 +495,7 @@ def to_row_dicts(self) -> Dict[str, Any]: sched_row = { "enabled": 1 if self.schedule.enabled else 0, "everyMinutes": int(getattr(self.schedule, "everyMinutes", compat_minutes) or compat_minutes), + "intervalSeconds": int(getattr(self.schedule, "interval_seconds", lambda: global_interval)()), "nextRunAt": self.schedule.nextRunAt, "lastScheduledRunAt": self.schedule.lastScheduledRunAt, "windows": json.dumps(self.schedule.windows or {}) if self.schedule.windows else None, @@ -810,6 +813,7 @@ def upsert(self, job: Job) -> int: "jobId": job_id, "enabled": int(schedule_row.get("enabled") or 0), "everyMinutes": int(schedule_row.get("everyMinutes") or 1), + "intervalSeconds": int(schedule_row.get("intervalSeconds") or 0), "nextRunAt": schedule_row.get("nextRunAt"), "lastScheduledRunAt": schedule_row.get("lastScheduledRunAt"), "windows": schedule_row.get("windows"), @@ -818,7 +822,7 @@ def upsert(self, job: Job) -> int: "schedule", s_data, ["jobId"], - update_columns=["enabled", "everyMinutes", "nextRunAt", "lastScheduledRunAt", "windows"], + update_columns=["enabled", "everyMinutes", "intervalSeconds", "nextRunAt", "lastScheduledRunAt", "windows"], ) return int(job.id or 0) diff --git a/src/replicator/replicator.py b/src/replicator/replicator.py index 7ae40f7..d821115 100644 --- a/src/replicator/replicator.py +++ b/src/replicator/replicator.py @@ -10,7 +10,7 @@ import shutil # Add datetime import for lastRun/lastResult -from datetime import datetime, timezone +from datetime import datetime, timezone, timedelta from PyQt5.QtCore import Qt from PyQt5.QtGui import QPixmap @@ -51,6 +51,161 @@ class Replicator(QMainWindow): # ------------------------------------------------------------------ + # ------------------------------------------------------------------ + # Scheduler (service mode) + # ------------------------------------------------------------------ + + def _parse_iso_dt(self, s: Any) -> Optional[datetime]: + if not s: + return None + try: + dt = datetime.fromisoformat(str(s)) + if dt.tzinfo is None: + dt = dt.replace(tzinfo=timezone.utc) + return dt + except Exception: + return None + + def _window_allows_now_local(self, windows: Dict[str, Any], now_local: datetime) -> bool: + """ + Determine if 'now_local' falls inside an allowed window for its weekday. + Windows are interpreted as LOCAL times (America/Montreal via system tz). + Supports overnight windows like 22:00-06:00. + If windows is empty/missing => allowed. + """ + if not windows or not isinstance(windows, dict): + return True + + wd = now_local.weekday() # 0..6 (Mon..Sun) + day_windows = windows.get(str(wd)) or windows.get(wd) + if not isinstance(day_windows, list) or not day_windows: + return False + + tnow = now_local.time().replace(tzinfo=None) + + def _parse_hhmm(val: Any) -> Optional[tuple[int, int]]: + try: + parts = str(val).strip().split(":") + if len(parts) != 2: + return None + hh = int(parts[0]); mm = int(parts[1]) + if hh < 0 or hh > 23 or mm < 0 or mm > 59: + return None + return hh, mm + except Exception: + return None + + for w in day_windows: + if not isinstance(w, dict): + continue + s = w.get("start"); e = w.get("end") + if not s or not e: + continue + ps = _parse_hhmm(s); pe = _parse_hhmm(e) + if ps is None or pe is None: + continue + sh, sm = ps; eh, em = pe + ts = datetime(2000, 1, 1, sh, sm).time() + te = datetime(2000, 1, 1, eh, em).time() + + if ts <= te: + if ts <= tnow <= te: + return True + else: + # overnight + if tnow >= ts or tnow <= te: + return True + + return False + + def _interval_seconds_for_schedule_row(self, sched_row: Dict[str, Any], now_local: datetime) -> int: + """ + Return interval seconds for this schedule row. + Priority: + 1) schedule.intervalSeconds column + 2) windows[weekday][0].intervalSeconds + 3) everyMinutes*60 + 4) service.defaultInterval + """ + default_iv = self._default_interval_seconds() + + try: + col = sched_row.get("intervalSeconds") + if col is not None: + iv = int(col or 0) + if iv > 0: + return iv + except Exception: + pass + + try: + windows = sched_row.get("windows") or {} + if isinstance(windows, str): + windows = json.loads(windows) if windows else {} + if isinstance(windows, dict): + wd = now_local.weekday() + day = windows.get(str(wd)) or windows.get(wd) + if isinstance(day, list) and day and isinstance(day[0], dict): + v = day[0].get("intervalSeconds") + if v is not None: + iv = int(v or 0) + if iv > 0: + return iv + except Exception: + pass + + try: + m = int(sched_row.get("everyMinutes", 60) or 60) + iv = m * 60 + return iv if iv > 0 else default_iv + except Exception: + return default_iv + + def _should_run_scheduled(self, job: Job, *, now_utc: datetime, now_local: datetime) -> bool: + """ + Decide if a job should run in SERVICE/SCHEDULED mode. + Manual 'Run now' intentionally bypasses this. + """ + if not job.id: + return False + + sched = self._db.one( + "SELECT enabled, everyMinutes, nextRunAt, lastScheduledRunAt, windows, intervalSeconds FROM schedule WHERE jobId=?", + (int(job.id),), + ) + if not sched: + # No schedule row => do not auto-run (manual run still works) + return False + + if not bool(sched.get("enabled", 0)): + return False + + # windows are local-time based + windows: Dict[str, Any] = {} + try: + raw = sched.get("windows") + if raw: + windows = json.loads(raw) if isinstance(raw, str) else (raw if isinstance(raw, dict) else {}) + except Exception: + windows = {} + + if not self._window_allows_now_local(windows, now_local): + return False + + # Respect nextRunAt if present + next_dt = self._parse_iso_dt(sched.get("nextRunAt")) + if next_dt and now_utc < next_dt: + return False + + interval_s = self._interval_seconds_for_schedule_row(sched, now_local) + last_dt = self._parse_iso_dt(sched.get("lastScheduledRunAt")) + + if last_dt is None: + # first scheduled run in an allowed window + return True + + elapsed = (now_utc - last_dt).total_seconds() + return elapsed >= float(interval_s) # Bidirectional sync helpers (local-only for now) # ------------------------------------------------------------------ @@ -560,7 +715,7 @@ def cli(self, cli: QApplication = None) -> None: if cli is None: return cli.add("run", "Run all replication jobs.", self.run) - cli.service.add("run", "Run all replication jobs.", self.run) + cli.service.add("run", "Run all replication jobs.", self.run_scheduled) # ------------------------------------------------------------------ # UI @@ -939,6 +1094,67 @@ def _run_with_ui_feedback(self): # Execution # ------------------------------------------------------------------ + def run_scheduled(self) -> bool: + """ + Scheduled/service execution entrypoint. + Respects per-job schedule (enabled/windows/interval). + Returns True if all eligible jobs succeeded. + """ + self._reload_jobs() + + verbose_db = False + try: + verbose_db = bool(self._configuration.get("log.verbose", False)) + except Exception: + verbose_db = False + self._db_debug_snapshot(verbose=verbose_db) + + if not self._jobs: + self._log("[Replicator] No jobs configured.", level="warning") + return False + + now_utc = datetime.now(timezone.utc) + now_local = now_utc.astimezone() # interpret windows as local times + + any_ran = False + all_ok = True + + for job in self._jobs: + if not bool(job.enabled): + continue + + if not self._should_run_scheduled(job, now_utc=now_utc, now_local=now_local): + continue + + any_ran = True + ok = self._run_job(job) + all_ok = all_ok and ok + + # Update schedule bookkeeping for this job + try: + sched_row = self._db.one( + "SELECT enabled, everyMinutes, nextRunAt, lastScheduledRunAt, windows, intervalSeconds FROM schedule WHERE jobId=?", + (int(job.id),), + ) or {} + interval_s = self._interval_seconds_for_schedule_row(sched_row, now_local) + next_run = (datetime.now(timezone.utc) + timedelta(seconds=int(interval_s))).isoformat() + self._db.update( + "schedule", + { + "lastScheduledRunAt": datetime.now(timezone.utc).isoformat(), + "nextRunAt": next_run, + }, + "jobId = :jobId", + {"jobId": int(job.id)}, + ) + except Exception: + pass + + if not any_ran: + self._log("[Replicator] No jobs due per schedule.", level="debug") + + return all_ok + def run(self) -> bool: """ Run all configured jobs (from DB). diff --git a/test.sh b/test.sh new file mode 100755 index 0000000..5608ffe --- /dev/null +++ b/test.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +touch tmp/a/loop-$(date +%H-%M).txt From 550b48130058c2cbdbd0ec2f2692320e8d6eabae Mon Sep 17 00:00:00 2001 From: Louis Ouellet Date: Fri, 16 Jan 2026 11:39:02 -0500 Subject: [PATCH 29/51] General: Updated corePY --- src/core | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core b/src/core index 9f00fb8..22edc9d 160000 --- a/src/core +++ b/src/core @@ -1 +1 @@ -Subproject commit 9f00fb8dc4bc66e1fdcd4f568fb23f1c6711a3d0 +Subproject commit 22edc9db282de01c57b70f5113d49a3fd1ef161f From 74a8d3bd3437a3f86b1add4f6ce82e906241fa85 Mon Sep 17 00:00:00 2001 From: Louis Ouellet Date: Fri, 16 Jan 2026 12:12:15 -0500 Subject: [PATCH 30/51] BUGFIX: Fixed the service mode and scheduling. --- src/replicator/job.py | 380 +++++++++++++++++------------------ src/replicator/migration.py | 44 +--- src/replicator/replicator.py | 55 ++--- src/replicator/ui.py | 31 +-- 4 files changed, 221 insertions(+), 289 deletions(-) diff --git a/src/replicator/job.py b/src/replicator/job.py index 47b6d48..a3d5eca 100644 --- a/src/replicator/job.py +++ b/src/replicator/job.py @@ -135,171 +135,218 @@ def from_db_row(row: Mapping[str, Any]) -> "Endpoint": return Endpoint(type=t, location=row.get("location") or "", auth=auth) -@dataclass(frozen=True) +@dataclass class Schedule: - """Job schedule. - - New model: - - intervalSeconds: global default interval - - windows: dict weekday -> list[window] - where window = {"start":"HH:mm","end":"HH:mm","intervalSeconds":int(optional)} - - Backward compatibility: - - everyMinutes is kept for legacy callers / older DB rows. - - If intervalSeconds is missing, it is derived from everyMinutes * 60. - """ - enabled: bool = True - - # New intervalSeconds: int = 3600 + windows: Dict[str, Any] = field(default_factory=dict) - # Legacy - everyMinutes: int = 60 - - # persisted as JSON in schedule.windows - windows: JsonDict = field(default_factory=dict) + def to_dict(self) -> Dict[str, Any]: + return { + "enabled": bool(self.enabled), + "intervalSeconds": int(self.intervalSeconds or 0), + "windows": self.windows if isinstance(self.windows, dict) else {}, + } - nextRunAt: Optional[str] = None - lastScheduledRunAt: Optional[str] = None + @staticmethod + def from_dict(d: Optional[Dict[str, Any]]) -> "Schedule": + d = d or {} + enabled = bool(d.get("enabled", True)) + # intervalSeconds can be on the schedule itself, or in per-day window objects + try: + interval_s = int(d.get("intervalSeconds") or 0) + except Exception: + interval_s = 0 + windows = d.get("windows") if isinstance(d.get("windows"), dict) else {} + if interval_s <= 0: + # best-effort: derive from first window on any day + try: + for _k, _v in windows.items(): + if isinstance(_v, list) and _v and isinstance(_v[0], dict): + v = _v[0].get("intervalSeconds") + if v is not None: + interval_s = int(v or 0) + break + except Exception: + pass + if interval_s <= 0: + interval_s = 3600 + return Schedule(enabled=enabled, intervalSeconds=interval_s, windows=windows) def validate(self) -> List[str]: errs: List[str] = [] if self.enabled: - if int(self.interval_seconds()) <= 0: + try: + iv = int(self.intervalSeconds or 0) + except Exception: + iv = 0 + if iv <= 0: errs.append("Schedule intervalSeconds must be > 0 when schedule is enabled.") + # windows is optional; if provided, validate structure best-effort if self.windows and not isinstance(self.windows, dict): - errs.append("Schedule windows must be a dict of weekday -> list[window].") - return errs + errs.append("Schedule windows must be an object/dict.") + return errs - def interval_seconds(self) -> int: - """Return global interval in seconds (always >= 1 when enabled).""" - try: - s = int(self.intervalSeconds or 0) - except Exception: - s = 0 - if s > 0: - return s - # fallback from legacy minutes - try: - m = int(self.everyMinutes or 0) - except Exception: - m = 0 - if m <= 0: - return 1 - return max(1, m * 60) + def _parse_hhmm(val: Any) -> Optional[Tuple[int, int]]: + try: + parts = str(val).strip().split(":") + if len(parts) != 2: + return None + hh = int(parts[0]); mm = int(parts[1]) + if hh < 0 or hh > 23 or mm < 0 or mm > 59: + return None + return (hh, mm) + except Exception: + return None - def _parse_hhmm(self, s: str) -> Optional[time]: try: - parts = s.strip().split(":") - if len(parts) != 2: - return None - hh = int(parts[0]) - mm = int(parts[1]) - if hh < 0 or hh > 23 or mm < 0 or mm > 59: - return None - return time(hour=hh, minute=mm) + for _day, arr in (self.windows or {}).items(): + if not isinstance(arr, list): + errs.append("Schedule windows entries must be lists.") + continue + for w in arr: + if not isinstance(w, dict): + errs.append("Schedule window must be an object.") + continue + if _parse_hhmm(w.get("start")) is None: + errs.append("Schedule window start must be HH:MM.") + if _parse_hhmm(w.get("end")) is None: + errs.append("Schedule window end must be HH:MM.") + if "intervalSeconds" in w: + try: + if int(w.get("intervalSeconds") or 0) <= 0: + errs.append("Schedule window intervalSeconds must be > 0 when provided.") + except Exception: + errs.append("Schedule window intervalSeconds must be an integer.") except Exception: - return None + # best-effort validation only + pass - def _day_windows(self, weekday: int) -> List[Dict[str, Any]]: - if not self.windows: - return [] - dw = self.windows.get(str(weekday)) - if dw is None: - dw = self.windows.get(weekday) - if not isinstance(dw, list): - return [] - out: List[Dict[str, Any]] = [] - for w in dw: - if isinstance(w, dict): - out.append(w) - return out - - def _interval_for_day(self, weekday: int) -> int: - """Return the intervalSeconds for a weekday. - - If windows are configured for that weekday and the first window includes intervalSeconds, use it. - Otherwise fall back to global intervalSeconds. - """ - day_ws = self._day_windows(weekday) - if day_ws: - w0 = day_ws[0] - if "intervalSeconds" in w0: - try: - v = int(w0.get("intervalSeconds") or 0) - if v > 0: - return v - except Exception: - pass - return self.interval_seconds() + return errs - def _in_window_for_day(self, dt: datetime) -> bool: - # No windows configured => always allowed. - if not self.windows: + def _window_allows_now_local(self, now_local: datetime) -> bool: + # If windows is empty/missing => allowed. + if not self.windows or not isinstance(self.windows, dict): return True - wd = dt.weekday() # 0..6 - day_windows = self._day_windows(wd) - if not day_windows: + wd = now_local.weekday() # 0..6 (Mon..Sun) + day_windows = self.windows.get(str(wd)) or self.windows.get(wd) + if not isinstance(day_windows, list) or not day_windows: return False - tnow = dt.timetz().replace(tzinfo=None) + tnow = now_local.time().replace(tzinfo=None) + + def _parse_hhmm(val: Any) -> Optional[Tuple[int, int]]: + try: + parts = str(val).strip().split(":") + if len(parts) != 2: + return None + hh = int(parts[0]); mm = int(parts[1]) + if hh < 0 or hh > 23 or mm < 0 or mm > 59: + return None + return (hh, mm) + except Exception: + return None for w in day_windows: - s = w.get("start") - e = w.get("end") - if not s or not e: + if not isinstance(w, dict): continue - ts = self._parse_hhmm(str(s)) - te = self._parse_hhmm(str(e)) - if ts is None or te is None: + ps = _parse_hhmm(w.get("start")) + pe = _parse_hhmm(w.get("end")) + if ps is None or pe is None: continue + sh, sm = ps; eh, em = pe + ts = datetime(2000, 1, 1, sh, sm).time() + te = datetime(2000, 1, 1, eh, em).time() if ts <= te: if ts <= tnow <= te: return True else: - # overnight window (e.g., 22:00 -> 06:00) + # overnight window (e.g. 22:00-06:00) if tnow >= ts or tnow <= te: return True return False + def interval_seconds_for_now(self, now: Optional[datetime] = None) -> int: + now_local = (now or datetime.now(timezone.utc)).astimezone() + # Priority: + # 1) per-day window intervalSeconds (first window entry) + # 2) schedule.intervalSeconds + try: + wd = now_local.weekday() + day = None + if isinstance(self.windows, dict): + day = self.windows.get(str(wd)) or self.windows.get(wd) + if isinstance(day, list) and day and isinstance(day[0], dict): + v = day[0].get("intervalSeconds") + if v is not None: + iv = int(v or 0) + if iv > 0: + return iv + except Exception: + pass + + try: + iv = int(self.intervalSeconds or 0) + return iv if iv > 0 else 3600 + except Exception: + return 3600 + def should_run_now(self, now: Optional[datetime] = None) -> bool: if not self.enabled: return False - # Windows are defined in local (wall-clock) time. - now = now or datetime.now().astimezone() - return self._in_window_for_day(now) + now_local = (now or datetime.now(timezone.utc)).astimezone() + return self._window_allows_now_local(now_local) def next_run_at(self, now: Optional[datetime] = None) -> Optional[datetime]: - """Compute the next aligned run time within allowed windows. - - Alignment is based on Unix epoch seconds modulo the intervalSeconds for that day. - Searches up to 7 days ahead. - """ + # This domain object doesn't store lastScheduledRunAt; caller should decide cadence. + # We return "now + interval" if windows allow now; otherwise the next allowed window start. if not self.enabled: return None - # Windows are defined in local (wall-clock) time. - now = now or datetime.now().astimezone() - # Start looking from next second (normalized) - cur = now.replace(microsecond=0) + timedelta(seconds=1) - - limit = cur + timedelta(days=7) - while cur <= limit: - if self._in_window_for_day(cur): - interval = self._interval_for_day(cur.weekday()) - if interval <= 0: - interval = 1 - try: - epoch_sec = int(cur.timestamp()) - except Exception: - epoch_sec = 0 - if (epoch_sec % int(interval)) == 0: - return cur - cur += timedelta(seconds=1) + now_utc = now or datetime.now(timezone.utc) + now_local = now_utc.astimezone() + + if self._window_allows_now_local(now_local): + return now_utc + timedelta(seconds=int(self.interval_seconds_for_now(now_utc))) + + # Find next allowed window start within the next 7 days (best-effort). + if not self.windows or not isinstance(self.windows, dict): + return now_utc + timedelta(seconds=int(self.interval_seconds_for_now(now_utc))) + + def _parse_hhmm(val: Any) -> Optional[Tuple[int, int]]: + try: + parts = str(val).strip().split(":") + if len(parts) != 2: + return None + hh = int(parts[0]); mm = int(parts[1]) + if hh < 0 or hh > 23 or mm < 0 or mm > 59: + return None + return (hh, mm) + except Exception: + return None + + base_local = now_local.replace(second=0, microsecond=0) + for add_days in range(0, 8): + day_local = base_local + timedelta(days=add_days) + wd = day_local.weekday() + day_windows = self.windows.get(str(wd)) or self.windows.get(wd) + if not isinstance(day_windows, list) or not day_windows: + continue + # use the first window as the start candidate (UI currently writes a single window per day) + w0 = day_windows[0] if isinstance(day_windows[0], dict) else None + if not w0: + continue + ps = _parse_hhmm(w0.get("start")) + if ps is None: + continue + sh, sm = ps + candidate_local = day_local.replace(hour=sh, minute=sm) + candidate_utc = candidate_local.astimezone(timezone.utc) + if candidate_utc > now_utc: + return candidate_utc return None @@ -479,26 +526,10 @@ def to_row_dicts(self) -> Dict[str, Any]: self.targetEndpoint.to_db_fields("target"), ] - # Persist the schedule windows as JSON; include per-day intervalSeconds inside windows. - # Keep everyMinutes for backward compatibility (best-effort conversion). - try: - global_interval = int(self.schedule.interval_seconds()) - except Exception: - # Fall back to the schedule's declared defaults rather than a magic number - try: - global_interval = int(getattr(self.schedule, "intervalSeconds", 3600) or 3600) - except Exception: - global_interval = 3600 - - compat_minutes = max(1, int(round(float(global_interval) / 60.0))) - sched_row = { - "enabled": 1 if self.schedule.enabled else 0, - "everyMinutes": int(getattr(self.schedule, "everyMinutes", compat_minutes) or compat_minutes), - "intervalSeconds": int(getattr(self.schedule, "interval_seconds", lambda: global_interval)()), - "nextRunAt": self.schedule.nextRunAt, - "lastScheduledRunAt": self.schedule.lastScheduledRunAt, - "windows": json.dumps(self.schedule.windows or {}) if self.schedule.windows else None, + "enabled": 1 if self.schedule and self.schedule.enabled else 0, + "intervalSeconds": int(self.schedule.intervalSeconds if self.schedule else 3600), + "windows": json.dumps(self.schedule.windows if self.schedule and isinstance(self.schedule.windows, dict) else {}), } return { @@ -542,57 +573,28 @@ def from_db_rows( j.targetEndpoint = tgt or Endpoint() if schedule_row: - windows: JsonDict = {} - raw_windows = schedule_row.get("windows") - if raw_windows: - try: - parsed = json.loads(raw_windows) - if isinstance(parsed, dict): - windows = parsed - except Exception: - windows = {} - - # Backward compatible interval handling: - # - Prefer intervalSeconds embedded in windows (per-day) - # - Else derive from everyMinutes - derived_seconds = None + # Read intervalSeconds and windows as new model, fallback as needed try: - # Try the first configured window's intervalSeconds - for _k, _v in (windows or {}).items(): - if isinstance(_v, list) and _v and isinstance(_v[0], dict) and "intervalSeconds" in _v[0]: - iv = int(_v[0].get("intervalSeconds") or 0) - if iv > 0: - derived_seconds = iv - break + interval_s = int(schedule_row.get("intervalSeconds") or 0) except Exception: - derived_seconds = None - - if derived_seconds is None: - try: - derived_seconds = int(schedule_row.get("everyMinutes", 60) or 60) * 60 - except Exception: - # Default to the Schedule dataclass default (intervalSeconds) rather than a scattered magic number - derived_seconds = int(getattr(Schedule, "__dataclass_fields__", {}).get("intervalSeconds").default) if hasattr(Schedule, "__dataclass_fields__") else 3600 - + interval_s = 0 + # Remove legacy everyMinutes fallback + if interval_s <= 0: + interval_s = 3600 + windows = {} try: - derived_seconds = int(derived_seconds or 0) + raw = schedule_row.get("windows") + if raw: + windows = json.loads(raw) if isinstance(raw, str) else (raw if isinstance(raw, dict) else {}) except Exception: - derived_seconds = 0 - if derived_seconds <= 0: - # fall back to Schedule default intervalSeconds - derived_seconds = int(getattr(Schedule, "__dataclass_fields__", {}).get("intervalSeconds").default) if hasattr(Schedule, "__dataclass_fields__") else 3600 - + windows = {} j.schedule = Schedule( enabled=bool(schedule_row.get("enabled", 1)), - intervalSeconds=int(derived_seconds), - everyMinutes=int(schedule_row.get("everyMinutes", 60) or 60), + intervalSeconds=int(interval_s), windows=windows, - nextRunAt=schedule_row.get("nextRunAt"), - lastScheduledRunAt=schedule_row.get("lastScheduledRunAt"), ) else: - # Use Schedule dataclass defaults (enabled/intervalSeconds/everyMinutes) when no DB row exists. - j.schedule = Schedule(windows={}) + j.schedule = Schedule() return j @@ -609,12 +611,7 @@ def to_legacy_dict(self) -> JsonDict: "conflictPolicy": self.conflictPolicy, "sourceEndpoint": {"type": self.sourceEndpoint.type, "location": self.sourceEndpoint.location, "auth": dict(self.sourceEndpoint.auth)}, "targetEndpoint": {"type": self.targetEndpoint.type, "location": self.targetEndpoint.location, "auth": dict(self.targetEndpoint.auth)}, - "schedule": { - "enabled": self.schedule.enabled, - "intervalSeconds": self.schedule.interval_seconds(), - "everyMinutes": self.schedule.everyMinutes, - "windows": self.schedule.windows, - }, + "schedule": self.schedule.to_dict() if self.schedule else {"enabled": True, "intervalSeconds": 3600, "windows": {}}, "lastRun": self.lastRun, "lastResult": self.lastResult, "lastError": self.lastError, @@ -812,17 +809,14 @@ def upsert(self, job: Job) -> int: s_data = { "jobId": job_id, "enabled": int(schedule_row.get("enabled") or 0), - "everyMinutes": int(schedule_row.get("everyMinutes") or 1), "intervalSeconds": int(schedule_row.get("intervalSeconds") or 0), - "nextRunAt": schedule_row.get("nextRunAt"), - "lastScheduledRunAt": schedule_row.get("lastScheduledRunAt"), "windows": schedule_row.get("windows"), } self._upsert( "schedule", s_data, ["jobId"], - update_columns=["enabled", "everyMinutes", "intervalSeconds", "nextRunAt", "lastScheduledRunAt", "windows"], + update_columns=["enabled", "intervalSeconds", "windows"], ) return int(job.id or 0) diff --git a/src/replicator/migration.py b/src/replicator/migration.py index 2259c7b..622fe89 100644 --- a/src/replicator/migration.py +++ b/src/replicator/migration.py @@ -170,11 +170,9 @@ def _migrations(self) -> List[Tuple[str, List[str]]]: created DATETIME DEFAULT CURRENT_TIMESTAMP, modified DATETIME DEFAULT CURRENT_TIMESTAMP, jobId INTEGER NOT NULL UNIQUE, - -- Default schedule: enabled, all day/every day via empty windows (interpreted as always allowed), - -- intervalSeconds=3600 (1h). Keep everyMinutes for backward compatibility. + -- Schedule is controlled via per-day windows and intervalSeconds. enabled INTEGER NOT NULL DEFAULT 1, intervalSeconds INTEGER NOT NULL DEFAULT 3600, - everyMinutes INTEGER NOT NULL DEFAULT 60, nextRunAt TEXT NULL, lastScheduledRunAt TEXT NULL, windows TEXT NULL, @@ -215,6 +213,8 @@ def _migrations(self) -> List[Tuple[str, List[str]]]: deleted INTEGER NOT NULL DEFAULT 0, deletedAt TEXT NULL, meta TEXT NULL, + lastSeenAt TEXT NULL, + lastSeenRunId INTEGER NULL, FOREIGN KEY(jobId) REFERENCES jobs(id) ON DELETE CASCADE ); """, @@ -243,18 +243,8 @@ def _migrations(self) -> List[Tuple[str, List[str]]]: ); """, "CREATE INDEX IF NOT EXISTS idx_conflicts_job_status ON conflicts(jobId, status);", - ], - ), - ( - "0002_file_state_last_seen", - [ - "ALTER TABLE file_state ADD COLUMN lastSeenAt TEXT NULL;", - "ALTER TABLE file_state ADD COLUMN lastSeenRunId INTEGER NULL;", - ], - ), - ( - "0003_meta_kv", - [ + + # meta (simple KV store) """ CREATE TABLE IF NOT EXISTS meta ( key TEXT PRIMARY KEY, @@ -265,28 +255,4 @@ def _migrations(self) -> List[Tuple[str, List[str]]]: """, ], ), - ( - "0004_schedule_windows", - [ - # Add windows if missing (SQLite has no IF NOT EXISTS for ADD COLUMN; ignore error if it already exists) - """ - SELECT 1; - """, - """ - -- handled in code: Migration will attempt the ALTER; if it fails due to duplicate column, it should be ignored. - """, - "ALTER TABLE schedule ADD COLUMN windows TEXT NULL;", - ], - ), - ( - "0005_schedule_interval_seconds", - [ - # Add global interval in seconds (new schedule model). Keep legacy everyMinutes for compatibility. - "ALTER TABLE schedule ADD COLUMN intervalSeconds INTEGER NOT NULL DEFAULT 3600;", - # Backfill intervalSeconds from everyMinutes where possible (existing rows). - "UPDATE schedule SET intervalSeconds = CASE WHEN intervalSeconds IS NULL OR intervalSeconds <= 0 THEN (everyMinutes * 60) ELSE intervalSeconds END;", - # Ensure enabled defaults to on for existing rows that still have the legacy default off (best-effort). - "UPDATE schedule SET enabled = 1 WHERE enabled IS NULL;", - ], - ), ] diff --git a/src/replicator/replicator.py b/src/replicator/replicator.py index d821115..8919d04 100644 --- a/src/replicator/replicator.py +++ b/src/replicator/replicator.py @@ -124,8 +124,7 @@ def _interval_seconds_for_schedule_row(self, sched_row: Dict[str, Any], now_loca Priority: 1) schedule.intervalSeconds column 2) windows[weekday][0].intervalSeconds - 3) everyMinutes*60 - 4) service.defaultInterval + 3) service.defaultInterval """ default_iv = self._default_interval_seconds() @@ -154,12 +153,7 @@ def _interval_seconds_for_schedule_row(self, sched_row: Dict[str, Any], now_loca except Exception: pass - try: - m = int(sched_row.get("everyMinutes", 60) or 60) - iv = m * 60 - return iv if iv > 0 else default_iv - except Exception: - return default_iv + return default_iv def _should_run_scheduled(self, job: Job, *, now_utc: datetime, now_local: datetime) -> bool: """ @@ -170,7 +164,7 @@ def _should_run_scheduled(self, job: Job, *, now_utc: datetime, now_local: datet return False sched = self._db.one( - "SELECT enabled, everyMinutes, nextRunAt, lastScheduledRunAt, windows, intervalSeconds FROM schedule WHERE jobId=?", + "SELECT enabled, nextRunAt, lastScheduledRunAt, windows, intervalSeconds FROM schedule WHERE jobId=?", (int(job.id),), ) if not sched: @@ -715,7 +709,7 @@ def cli(self, cli: QApplication = None) -> None: if cli is None: return cli.add("run", "Run all replication jobs.", self.run) - cli.service.add("run", "Run all replication jobs.", self.run_scheduled) + cli.service.add("run", "Run all replication jobs", self.run_scheduled, interval=1) # ------------------------------------------------------------------ # UI @@ -895,7 +889,7 @@ def _schedule_interval_seconds_for_now(self, sched: Dict[str, Any]) -> int: """Best-effort interval seconds for logging (supports per-day interval stored in window dicts).""" try: windows = sched.get("windows") if isinstance(sched.get("windows"), dict) else {} - wd = datetime.now(timezone.utc).weekday() + wd = datetime.now().astimezone().weekday() day = windows.get(str(wd)) or windows.get(wd) # type: ignore[index] if isinstance(day, list) and day and isinstance(day[0], dict): v = day[0].get("intervalSeconds") @@ -912,11 +906,8 @@ def _schedule_interval_seconds_for_now(self, sched: Dict[str, Any]) -> int: return v if v > 0 else default_interval except Exception: pass - try: - v = int(sched.get("everyMinutes", 60) or 60) * 60 - return v if v > 0 else default_interval - except Exception: - return default_interval + + return default_interval def _job_from_legacy_dict(self, d: Dict[str, Any], *, existing_id: Optional[int] = None) -> Job: src = d.get("sourceEndpoint") or {"type": "local", "location": d.get("source", ""), "auth": {}} @@ -932,27 +923,28 @@ def _job_from_legacy_dict(self, d: Dict[str, Any], *, existing_id: Optional[int] default_interval = self._default_interval_seconds() - # Backward compatibility: accept older key `everyMinutes` - if "intervalSeconds" not in sched: - try: - v = int(sched.get("everyMinutes", 60) or 60) * 60 - sched["intervalSeconds"] = v if v > 0 else default_interval - except Exception: - sched["intervalSeconds"] = default_interval + # Normalize intervalSeconds (no everyMinutes support) + try: + iv = int(sched.get("intervalSeconds") or 0) + except Exception: + iv = 0 + if iv <= 0: + iv = default_interval + sched["intervalSeconds"] = iv # Ensure windows exist; if missing, make it "every day, all day" with per-day interval windows = sched.get("windows") if not isinstance(windows, dict) or not windows: windows = {} for wd in range(7): - windows[str(wd)] = [{"start": "00:00", "end": "23:59", "intervalSeconds": int(sched.get("intervalSeconds") or default_interval)}] + windows[str(wd)] = [{"start": "00:00", "end": "23:59", "intervalSeconds": iv}] sched["windows"] = windows else: # Ensure each enabled day window includes intervalSeconds (per-day) for the upcoming scheduler logic for k, v in list(windows.items()): if isinstance(v, list) and v and isinstance(v[0], dict): if "intervalSeconds" not in v[0]: - v[0]["intervalSeconds"] = int(sched.get("intervalSeconds") or default_interval) + v[0]["intervalSeconds"] = iv j = Job( id=existing_id, @@ -982,18 +974,9 @@ def _job_from_legacy_dict(self, d: Dict[str, Any], *, existing_id: Optional[int] windows = sched.get("windows") if isinstance(sched.get("windows"), dict) else {} - # NOTE: The domain model currently uses `everyMinutes`. - # For now, we map intervalSeconds -> everyMinutes (rounded down, minimum 1) for compatibility. - interval_seconds = default_interval - try: - interval_seconds = int(sched.get("intervalSeconds") or default_interval) - except Exception: - interval_seconds = default_interval - every_minutes = max(1, int(interval_seconds // 60)) - j.schedule = Schedule( enabled=bool(sched.get("enabled", True)), - everyMinutes=every_minutes, + intervalSeconds=int(iv), windows=windows, ) @@ -1133,7 +1116,7 @@ def run_scheduled(self) -> bool: # Update schedule bookkeeping for this job try: sched_row = self._db.one( - "SELECT enabled, everyMinutes, nextRunAt, lastScheduledRunAt, windows, intervalSeconds FROM schedule WHERE jobId=?", + "SELECT enabled, nextRunAt, lastScheduledRunAt, windows, intervalSeconds FROM schedule WHERE jobId=?", (int(job.id),), ) or {} interval_s = self._interval_seconds_for_schedule_row(sched_row, now_local) diff --git a/src/replicator/ui.py b/src/replicator/ui.py index e600e74..f0e5c71 100644 --- a/src/replicator/ui.py +++ b/src/replicator/ui.py @@ -667,7 +667,7 @@ class ScheduleDialog(QDialog): Persists as: schedule = { "enabled": bool, - "intervalSeconds": int, # global default/fallback + "intervalSeconds": int, # computed fallback (minimum per-day interval if enabled, else default) "windows": { "0":[{"start":"22:00","end":"06:00","intervalSeconds":3600}], ... @@ -707,12 +707,6 @@ def __init__(self, parent=None, job: Optional[Dict[str, Any]] = None, *, default for wd in range(7): schedule["windows"][str(wd)] = [{"start": "00:00", "end": "23:59", "intervalSeconds": int(self._default_interval_seconds)}] else: - # Backward compatibility: everyMinutes -> intervalSeconds - if "intervalSeconds" not in schedule: - try: - schedule["intervalSeconds"] = int(schedule.get("everyMinutes", 60) or 60) * 60 - except Exception: - schedule["intervalSeconds"] = int(self._default_interval_seconds) # Ensure windows exists; if missing, default to every day all day if not isinstance(schedule.get("windows"), dict) or not schedule.get("windows"): schedule["windows"] = {} @@ -739,12 +733,7 @@ def __init__(self, parent=None, job: Optional[Dict[str, Any]] = None, *, default self.enabled = QCheckBox() self.enabled.setChecked(bool(schedule.get("enabled", False))) - self.interval_seconds = QSpinBox() - self.interval_seconds.setRange(1, 604800) # 1s .. 7 days - self.interval_seconds.setValue(int(schedule.get("intervalSeconds", self._default_interval_seconds) or self._default_interval_seconds)) - form.addRow("Enabled", self.enabled) - form.addRow("Interval (in seconds)", self.interval_seconds) # Windows editor win_frame = QFrame() @@ -837,7 +826,6 @@ def _apply_enabled_state(_=None, *, _day_enabled=day_enabled, _start=start, _end def _sync_enabled_state(self): en = self.enabled.isChecked() - self.interval_seconds.setEnabled(en) for wd, ctrls in self._day_controls.items(): ctrls["enabled"].setEnabled(en) # if schedule disabled, visually disable time edits too @@ -866,10 +854,6 @@ def _parse_qtime(self, s: str, default: QTime) -> QTime: return default def _on_ok(self): - if self.enabled.isChecked() and self.interval_seconds.value() < 1: - MsgBox.show(self, "Schedule", "Interval (in seconds) must be at least 1 if enabled.", icon="warning") - return - if self.enabled.isChecked(): for _wd, ctrls in self._day_controls.items(): if not ctrls["enabled"].isChecked(): @@ -884,7 +868,7 @@ def _on_ok(self): def value(self) -> Dict[str, Any]: windows: Dict[str, Any] = {} - + enabled_intervals = [] if self.enabled.isChecked(): for wd, ctrls in self._day_controls.items(): if not ctrls["enabled"].isChecked(): @@ -893,11 +877,16 @@ def value(self) -> Dict[str, Any]: e = ctrls["end"].time().toString("HH:mm") itv = int(ctrls["interval"].value()) windows[str(wd)] = [{"start": s, "end": e, "intervalSeconds": itv}] + enabled_intervals.append(itv) + + # Compute fallback intervalSeconds: min per-day interval if enabled, else default + if self.enabled.isChecked() and enabled_intervals: + computed_interval_seconds = min(enabled_intervals) + else: + computed_interval_seconds = int(self._default_interval_seconds) return { "enabled": bool(self.enabled.isChecked()), - "intervalSeconds": int(self.interval_seconds.value()), - # Backward compatibility (old key) for any older code paths: - "everyMinutes": max(1, int(int(self.interval_seconds.value()) // 60)), + "intervalSeconds": int(computed_interval_seconds), "windows": windows, } From f145eb58ea11521ae5752675ac9d5444376226a7 Mon Sep 17 00:00:00 2001 From: Louis Ouellet Date: Mon, 19 Jan 2026 10:54:17 -0500 Subject: [PATCH 31/51] General: Updated corePY --- src/core | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core b/src/core index 22edc9d..c3fb63c 160000 --- a/src/core +++ b/src/core @@ -1 +1 @@ -Subproject commit 22edc9db282de01c57b70f5113d49a3fd1ef161f +Subproject commit c3fb63c90b5f9ce0adf2a27d6828de4d726cc7c2 From d666e76fa85e51ff9313d660247e2d2222507c5f Mon Sep 17 00:00:00 2001 From: Louis Ouellet Date: Mon, 19 Jan 2026 11:25:15 -0500 Subject: [PATCH 32/51] General: Added the application name below the logo. --- README.md | 4 +- src/replicator/replicator.py | 120 +++++++++++++++++++---------------- 2 files changed, 66 insertions(+), 58 deletions(-) diff --git a/README.md b/README.md index 7af9d70..d7f920e 100644 --- a/README.md +++ b/README.md @@ -37,9 +37,9 @@ Contributions to Replicator are welcome! If you have ideas for new features or h - ~~**Mode Mirror**: Add replication mode Mirror.~~ - **Mode Incremental**: Add replication mode Incremental. - ~~**Direction**: Add replication direction (One way, Two way).~~ - - **Scheduling**: Add scheduling capabilities for replication tasks. + - ~~**Scheduling**: Add scheduling capabilities for replication tasks.~~ - ~~**Conflict Resolution**: Implement conflict resolution strategies for file changes.~~ - ~~**Service Integration**: Integrate with system services for background operation.~~ ## Wait, where is the documentation? -Review the [Documentation](https://laswitchtech.com/en/blog/projects/pyrdpconnect/index). +Review the [Documentation](https://laswitchtech.com/en/blog/projects/replicator/index). diff --git a/src/replicator/replicator.py b/src/replicator/replicator.py index 8919d04..794b14b 100644 --- a/src/replicator/replicator.py +++ b/src/replicator/replicator.py @@ -51,6 +51,60 @@ class Replicator(QMainWindow): # ------------------------------------------------------------------ + # Initialization + # ------------------------------------------------------------------ + + def __init__( + self, + helper: Optional[Helper] = None, + configuration: Optional[Configuration] = None, + logger: Optional[Log] = None, + ): + super().__init__() + + self._app = QApplication.instance() + if self._app is None: + raise RuntimeError("Replicator must be created after QApplication/Application.") + + helper = helper or getattr(self._app, "helper", None) + configuration = configuration or getattr(self._app, "configuration", None) + logger = logger or getattr(self._app, "logger", None) + + if helper is None or configuration is None: + raise RuntimeError("Replicator requires corePY Helper + Configuration.") + + self._helper: Helper = helper + self._configuration: Configuration = configuration + self._logger: Optional[Log] = logger + + self._fs = FileSystem(helper=self._helper, logger=self._logger) + + # --- Database path setup --- + # Use Helper.get_cwd() if present, else os.getcwd() + if hasattr(self._helper, "get_cwd") and callable(getattr(self._helper, "get_cwd", None)): + base_dir = self._helper.get_cwd() + else: + base_dir = os.getcwd() + data_dir = os.path.join(base_dir, "data") + db_path = os.path.join(data_dir, "replicator.db") + + # --- Database (corePY SQLite wrapper) + migrations --- + self._db = SQLite(db_path=db_path) + self._migration = Migration(self._db, logger=self._logger) + self._migration.ensure() + + self._store = JobStore(self._db) + + self._log(f"[Replicator] Database: {db_path}", level="debug") + self._log("[Replicator] Database migrations applied.", level="info") + + # Domain jobs (Job objects) + self._jobs: List[Job] = [] + self._table: Optional[QTableWidget] = None + + # After DB is ready, log a snapshot of DB layout/counts (non-verbose) + self._db_debug_snapshot(verbose=False) + # ------------------------------------------------------------------ # Scheduler (service mode) # ------------------------------------------------------------------ @@ -643,60 +697,6 @@ def _winner_newest(a: Dict[str, Any], b: Dict[str, Any]) -> str: pass return True, stats - """ - Replicator UI + CLI entrypoint. - Jobs are stored in SQLite database (see ReplicatorDB). - """ - def __init__( - self, - helper: Optional[Helper] = None, - configuration: Optional[Configuration] = None, - logger: Optional[Log] = None, - ): - super().__init__() - - self._app = QApplication.instance() - if self._app is None: - raise RuntimeError("Replicator must be created after QApplication/Application.") - - helper = helper or getattr(self._app, "helper", None) - configuration = configuration or getattr(self._app, "configuration", None) - logger = logger or getattr(self._app, "logger", None) - - if helper is None or configuration is None: - raise RuntimeError("Replicator requires corePY Helper + Configuration.") - - self._helper: Helper = helper - self._configuration: Configuration = configuration - self._logger: Optional[Log] = logger - - self._fs = FileSystem(helper=self._helper, logger=self._logger) - - # --- Database path setup --- - # Use Helper.get_cwd() if present, else os.getcwd() - if hasattr(self._helper, "get_cwd") and callable(getattr(self._helper, "get_cwd", None)): - base_dir = self._helper.get_cwd() - else: - base_dir = os.getcwd() - data_dir = os.path.join(base_dir, "data") - db_path = os.path.join(data_dir, "replicator.db") - - # --- Database (corePY SQLite wrapper) + migrations --- - self._db = SQLite(db_path=db_path) - self._migration = Migration(self._db, logger=self._logger) - self._migration.ensure() - - self._store = JobStore(self._db) - - self._log(f"[Replicator] Database: {db_path}", level="debug") - self._log("[Replicator] Database migrations applied.", level="info") - - # Domain jobs (Job objects) - self._jobs: List[Job] = [] - self._table: Optional[QTableWidget] = None - - # After DB is ready, log a snapshot of DB layout/counts (non-verbose) - self._db_debug_snapshot(verbose=False) # ------------------------------------------------------------------ # CLI integration @@ -738,16 +738,24 @@ def init(self): # Use app icon/logo if you have one; otherwise harmless # Adjust path to wherever you store icons in Replicator candidate = self._helper.get_path("icons/icon.png") or self._helper.get_path("core/icons/info.svg") + name = getattr(self._app, "name", None) or self._app.applicationName() if candidate and self._helper.file_exists(candidate) and candidate.lower().endswith(".png"): pm = QPixmap(candidate) if not pm.isNull(): logo.setPixmap(pm.scaled(256, 256, Qt.KeepAspectRatio, Qt.SmoothTransformation)) else: - logo.setText("Replicator") - logo.setStyleSheet("font-size: 22px; font-weight: 600;") + logo.setText(name) + logo.setStyleSheet("font-size: 32px; font-weight: 200; opacity: 0.8; padding: 0px; margin: 0px;") root.addWidget(logo) + # Application name (under logo) + app_name = QLabel() + app_name.setText(str(name) if name else "") + app_name.setAlignment(Qt.AlignCenter) + app_name.setStyleSheet("font-size: 32px; font-weight: 200; opacity: 0.8; padding: 0px; margin: 0px;") + root.addWidget(app_name) + # --- Actions row (top, after logo) --- actions_row = QHBoxLayout() # No stretch at start; left-aligned by default From 6782b1b9dea908ae7b90e7f3ae232b4f962adab8 Mon Sep 17 00:00:00 2001 From: Louis Ouellet Date: Mon, 19 Jan 2026 11:47:59 -0500 Subject: [PATCH 33/51] General: Updated corePY --- src/core | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core b/src/core index c3fb63c..fe3ba4c 160000 --- a/src/core +++ b/src/core @@ -1 +1 @@ -Subproject commit c3fb63c90b5f9ce0adf2a27d6828de4d726cc7c2 +Subproject commit fe3ba4cc8593db38ab7c9f75e4861b91395a849f From 26f6866e6614c23897bc4a8c571a365f46092330 Mon Sep 17 00:00:00 2001 From: Louis Ouellet Date: Tue, 20 Jan 2026 09:43:04 -0500 Subject: [PATCH 34/51] General: Added support for corePY.filesystem.share. --- src/replicator/job.py | 314 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 311 insertions(+), 3 deletions(-) diff --git a/src/replicator/job.py b/src/replicator/job.py index a3d5eca..156cb56 100644 --- a/src/replicator/job.py +++ b/src/replicator/job.py @@ -5,10 +5,21 @@ from dataclasses import dataclass, field from datetime import datetime, timedelta, timezone, time -from typing import Any, Callable, Dict, Iterable, List, Mapping, Optional, Tuple +from typing import Any, Callable, Dict, Iterable, List, Mapping, Optional, Tuple, Union import json import sqlite3 +import tempfile +import time +import shutil +import subprocess +from pathlib import Path +from urllib.parse import urlparse + +import os +import sys +import platform + try: # corePY SQLite wrapper (preferred) from core.database.sqlite import SQLite # type: ignore @@ -16,9 +27,280 @@ SQLite = None # type: ignore + JsonDict = Dict[str, Any] +# --------------------------------------------------------------------------- +# Remote endpoints (rclone mount) +# --------------------------------------------------------------------------- + +class RemoteMountError(RuntimeError): + pass + + +def _is_windows() -> bool: + return (os.name == "nt") + + +def _find_rclone() -> str: + """Resolve rclone from PATH or bundled src/bin layout.""" + exe = "rclone.exe" if _is_windows() else "rclone" + + p = shutil.which(exe) + if p: + return p + + # Bundled layout expected by corePY-style projects: + # src/bin/rclone/[os]/[arch]/bin/rclone + here = Path(__file__).resolve() + os_name = "windows" if _is_windows() else ("macos" if sys.platform == "darwin" else "linux") + + mach = (platform.machine() or "").lower() + if mach in {"x86_64", "amd64"}: + arch = "x86_64" + elif mach in {"aarch64", "arm64"}: + arch = "arm64" + elif mach.startswith("arm"): + arch = "arm" + elif mach in {"i386", "i686", "x86"}: + arch = "x86" + else: + arch = mach or "unknown" + + for parent in [here.parent] + list(here.parents): + candidate = parent / "src" / "bin" / "rclone" / os_name / arch / "bin" / exe + if candidate.exists() and candidate.is_file(): + return str(candidate) + + raise RemoteMountError("rclone not found (not in PATH and not bundled under src/bin/rclone).") + + +def _rclone_escape(val: str) -> str: + # Escape separators used by rclone on-the-fly remotes. + return str(val).replace("\\", "\\\\").replace(",", "\\,").replace(":", "\\:") + + +def _parse_smb_location(location: str) -> Tuple[str, str, str]: + """Return (host, share, subpath) from location. + + Accepts forms: + - //host/share/subpath + - \\host\\share\\subpath + - host/share/subpath + """ + loc = (location or "").strip() + loc = loc.replace("\\", "/") + loc = loc.lstrip("/") + parts = [p for p in loc.split("/") if p] + if len(parts) < 2: + raise RemoteMountError("SMB location must include host and share (e.g. //server/Share[/path]).") + host = parts[0] + share = parts[1] + subpath = "/".join(parts[2:]) if len(parts) > 2 else "" + return host, share, subpath + + +def _parse_host_path_location(location: str, default_scheme: str) -> Tuple[str, str, Optional[str]]: + """Parse host/path from either URL form or host:/path. + + Returns (host, path, user_from_location). + + Accepts: + - ftp://host/path + - sftp://user@host:22/path + - user@host:/path + - host:/path + """ + loc = (location or "").strip() + + # URL form + if "://" in loc: + u = urlparse(loc) + host = u.hostname or "" + path = u.path or "/" + user = u.username + if not host: + raise RemoteMountError(f"Invalid {default_scheme} URL (missing host): {location}") + return host, path, user + + # scp-ish form + if ":" in loc: + left, right = loc.split(":", 1) + user = None + host = left + if "@" in left: + user, host = left.split("@", 1) + path = right if right.startswith("/") else ("/" + right) + if not host: + raise RemoteMountError(f"Invalid {default_scheme} location (missing host): {location}") + return host, path, user + + raise RemoteMountError( + f"Invalid {default_scheme} location. Use host:/path or {default_scheme}://host/path (got: {location})" + ) + + +def _build_rclone_remote(endpoint: "Endpoint") -> Tuple[str, str]: + """Return (remote_def, mount_subpath_description). + + mount_subpath_description is informational and currently unused. + """ + t = (endpoint.type or "local").lower() + auth = dict(endpoint.auth or {}) + + if t == "smb": + host, share, subpath = _parse_smb_location(endpoint.location) + params: Dict[str, str] = { + "host": host, + "share": share, + } + # Optional path within share + if subpath: + params["path"] = subpath + + # Credentials + username = str(auth.get("username") or "") + password = str(auth.get("password") or "") + domain = str(auth.get("domain") or "") + guest = bool(auth.get("guest", True)) + + if not guest and username: + params["user"] = username + if not guest and password: + params["pass"] = password + if domain: + params["domain"] = domain + + remote = ":smb," + ",".join(f"{k}={_rclone_escape(v)}" for k, v in params.items()) + ":" + return remote, subpath + + if t == "ftp": + host, path, user_from_loc = _parse_host_path_location(endpoint.location, "ftp") + params = { + "host": host, + "path": path, + } + try: + params["port"] = str(int(auth.get("port", 21))) + except Exception: + params["port"] = "21" + + username = str(auth.get("username") or user_from_loc or "") + password = str(auth.get("password") or "") + guest = bool(auth.get("guest", False)) + if not guest and username: + params["user"] = username + if not guest and password: + params["pass"] = password + + remote = ":ftp," + ",".join(f"{k}={_rclone_escape(v)}" for k, v in params.items()) + ":" + return remote, path + + if t == "ssh": + host, path, user_from_loc = _parse_host_path_location(endpoint.location, "sftp") + params = { + "host": host, + "path": path, + } + try: + params["port"] = str(int(auth.get("port", 22))) + except Exception: + params["port"] = "22" + + username = str(auth.get("username") or user_from_loc or "") + password = str(auth.get("password") or "") + use_key = bool(auth.get("useKey", False)) + key_path = str(auth.get("key") or "") + + if username: + params["user"] = username + if (not use_key) and password: + params["pass"] = password + if use_key and key_path: + # rclone sftp backend uses key_file + params["key_file"] = key_path + + remote = ":sftp," + ",".join(f"{k}={_rclone_escape(v)}" for k, v in params.items()) + ":" + return remote, path + + raise RemoteMountError(f"Unsupported remote endpoint type for mount: {endpoint.type}") + + +from dataclasses import dataclass + +@dataclass +class _MountedEndpoint: + local_path: str + mount_point: Optional[str] = None + proc: Optional[subprocess.Popen] = None + + def cleanup(self) -> None: + if self.mount_point: + try: + rclone = _find_rclone() + subprocess.run([rclone, "unmount", self.mount_point], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) + except Exception: + pass + if self.proc: + try: + self.proc.terminate() + except Exception: + pass + + +def _mount_endpoint_if_remote(endpoint: "Endpoint", job_id: Union[int, None], role: str) -> _MountedEndpoint: + """If endpoint is remote (smb/ftp/ssh), mount it and return local path; otherwise return local location.""" + t = (endpoint.type or "local").lower() + if t == "local": + return _MountedEndpoint(local_path=endpoint.location) + + if t not in ("smb", "ftp", "ssh"): + raise RemoteMountError(f"Unsupported endpoint type: {t}") + + rclone = _find_rclone() + remote_def, _ = _build_rclone_remote(endpoint) + + # Mount under a stable temp folder for the job + base = Path(tempfile.gettempdir()) / "replicator" / "mounts" / (str(job_id or "new")) / role + base.mkdir(parents=True, exist_ok=True) + mount_point = str(base) + + cmd: List[str] = [rclone, "mount", remote_def, mount_point] + + # Best-effort background on Unix. On Windows, we keep it in a subprocess. + if not _is_windows(): + cmd += ["--daemon"] + + # Some rclone builds may require --vfs-cache-mode for better behavior; allow user to set via auth/options + # If user provided extra rclone args in auth["rcloneArgs"] as a list, append them. + extra_args = endpoint.auth.get("rcloneArgs") if isinstance(endpoint.auth, dict) else None + if isinstance(extra_args, list): + for a in extra_args: + if isinstance(a, str) and a.strip(): + cmd.append(a.strip()) + + proc: Optional[subprocess.Popen] = None + if _is_windows(): + # Start in background; rclone mount will keep running. + proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) + else: + # With --daemon, rclone exits quickly; no need to keep a proc. + subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) + + # Wait briefly for mount to become available. + deadline = time.time() + 10.0 + while time.time() < deadline: + try: + if os.path.isdir(mount_point) and (os.path.ismount(mount_point) or os.listdir(mount_point) is not None): + break + except Exception: + pass + time.sleep(0.25) + + return _MountedEndpoint(local_path=mount_point, mount_point=mount_point, proc=proc) + + # --------------------------------------------------------------------------- # Data models # --------------------------------------------------------------------------- @@ -71,6 +353,12 @@ def to_db_fields(self, role: str) -> JsonDict: port = int(auth.get("port", 21)) except Exception: port = 21 + # Optional SMB domain + if t == "smb": + options["domain"] = auth.get("domain") + # Optional rclone args (applies to all remote types) + if isinstance(auth.get("rcloneArgs"), list): + options["rcloneArgs"] = auth.get("rcloneArgs") elif t == "ssh": useKey = 1 if bool(auth.get("useKey", False)) else 0 username = auth.get("username") or None @@ -80,6 +368,8 @@ def to_db_fields(self, role: str) -> JsonDict: except Exception: port = 22 sshKey = auth.get("key") or None + if isinstance(auth.get("rcloneArgs"), list): + options["rcloneArgs"] = auth.get("rcloneArgs") known_keys = {"guest", "username", "password", "port", "useKey", "key"} for k, v in auth.items(): @@ -114,6 +404,9 @@ def from_db_row(row: Mapping[str, Any]) -> "Endpoint": } if t == "ftp": auth["port"] = row.get("port") or 21 + if t == "smb": + # domain is stored in options JSON when present + pass elif t == "ssh": auth = { "useKey": bool(row.get("useKey", 0)), @@ -460,11 +753,19 @@ def _log(msg: str, level: str = "info") -> None: stats={}, ) - src = self.sourceEndpoint.location - dst = self.targetEndpoint.location preserve = bool(self.preserveMetadata) allow_del = bool(self.allowDeletion) + # Resolve endpoints (mount remote endpoints to local paths via rclone) + mounted: List[_MountedEndpoint] = [] + src_m = _mount_endpoint_if_remote(self.sourceEndpoint, self.id, "source") + mounted.append(src_m) + dst_m = _mount_endpoint_if_remote(self.targetEndpoint, self.id, "target") + mounted.append(dst_m) + + src = src_m.local_path + dst = dst_m.local_path + ok: bool = False stats: JsonDict = {} @@ -487,6 +788,13 @@ def _log(msg: str, level: str = "info") -> None: ok = False self.lastError = str(e) _log(f"[Job] Execution failed: {e}", "error") + finally: + # Always unmount remote endpoints + for m in reversed(mounted): + try: + m.cleanup() + except Exception: + pass ended_at = datetime.now(timezone.utc).isoformat() self.lastRun = ended_at From b3fbe6c7ec357ebb144ab5182fcf714eec077abb Mon Sep 17 00:00:00 2001 From: Louis Ouellet Date: Tue, 20 Jan 2026 09:44:23 -0500 Subject: [PATCH 35/51] General: Updated corePY --- src/core | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core b/src/core index fe3ba4c..ae11190 160000 --- a/src/core +++ b/src/core @@ -1 +1 @@ -Subproject commit fe3ba4cc8593db38ab7c9f75e4861b91395a849f +Subproject commit ae111905945666c9023fff2baf309031baba8171 From 61a1a5f735d96a5b3229d4eb4ae2a97672475104 Mon Sep 17 00:00:00 2001 From: Louis Ouellet Date: Tue, 20 Jan 2026 09:45:45 -0500 Subject: [PATCH 36/51] BUGFIX: Updated the test script. --- test.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test.sh b/test.sh index 5608ffe..4d981c8 100755 --- a/test.sh +++ b/test.sh @@ -1,3 +1,3 @@ #!/bin/bash -touch tmp/a/loop-$(date +%H-%M).txt +touch tmp/a/file-$(date +%H-%M).txt From d6c428586ecb3e7878ce147be564671e21e9395e Mon Sep 17 00:00:00 2001 From: Louis Ouellet Date: Tue, 20 Jan 2026 10:37:34 -0500 Subject: [PATCH 37/51] General: Updated corePY --- src/core | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core b/src/core index ae11190..0158f28 160000 --- a/src/core +++ b/src/core @@ -1 +1 @@ -Subproject commit ae111905945666c9023fff2baf309031baba8171 +Subproject commit 0158f28269fab3e61ff9e45397fc208c6c56195b From e3ea0ffa77c7f843c4ce7f2ed564e282353e3208 Mon Sep 17 00:00:00 2001 From: Louis Ouellet Date: Tue, 20 Jan 2026 10:38:16 -0500 Subject: [PATCH 38/51] BUGFIX: Attempting to debug and correct the process of transfering files through network shares. --- src/replicator/job.py | 96 ++++++++++-- src/replicator/replicator.py | 295 ++++++++++++++++++++++++++++++++++- 2 files changed, 371 insertions(+), 20 deletions(-) diff --git a/src/replicator/job.py b/src/replicator/job.py index 156cb56..43f54f2 100644 --- a/src/replicator/job.py +++ b/src/replicator/job.py @@ -236,15 +236,23 @@ class _MountedEndpoint: proc: Optional[subprocess.Popen] = None def cleanup(self) -> None: - if self.mount_point: + # On Windows, the mount is typically held by the running rclone process. + if self.proc: try: - rclone = _find_rclone() - subprocess.run([rclone, "unmount", self.mount_point], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) + self.proc.terminate() except Exception: pass - if self.proc: + + if self.mount_point: try: - self.proc.terminate() + rclone = _find_rclone() + subprocess.run( + [rclone, "unmount", self.mount_point], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + text=True, + timeout=15, + ) except Exception: pass @@ -261,12 +269,29 @@ def _mount_endpoint_if_remote(endpoint: "Endpoint", job_id: Union[int, None], ro rclone = _find_rclone() remote_def, _ = _build_rclone_remote(endpoint) + # Preflight: validate that rclone can list the remote. + # This catches bad credentials / unreachable host early and provides a useful error. + try: + pre = subprocess.run( + [rclone, "lsf", remote_def, "--max-depth", "1"], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + text=True, + timeout=20, + ) + if pre.returncode != 0: + err = (pre.stderr or pre.stdout or "").strip() + raise RemoteMountError(f"rclone preflight failed for {endpoint.type}: {err or 'unknown error'}") + except subprocess.TimeoutExpired: + raise RemoteMountError(f"rclone preflight timed out for {endpoint.type} endpoint") + # Mount under a stable temp folder for the job base = Path(tempfile.gettempdir()) / "replicator" / "mounts" / (str(job_id or "new")) / role base.mkdir(parents=True, exist_ok=True) mount_point = str(base) cmd: List[str] = [rclone, "mount", remote_def, mount_point] + cmd += ["--ask-password", "false"] # Best-effort background on Unix. On Windows, we keep it in a subprocess. if not _is_windows(): @@ -284,20 +309,38 @@ def _mount_endpoint_if_remote(endpoint: "Endpoint", job_id: Union[int, None], ro if _is_windows(): # Start in background; rclone mount will keep running. proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) + # Give it a moment to fail fast if something is wrong. + time.sleep(0.5) + if proc.poll() is not None and proc.returncode not in (None, 0): + try: + err = (proc.stderr.read() if proc.stderr else "") + except Exception: + err = "" + raise RemoteMountError(f"rclone mount failed (windows): {(err or '').strip() or 'unknown error'}") else: - # With --daemon, rclone exits quickly; no need to keep a proc. - subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) - - # Wait briefly for mount to become available. - deadline = time.time() + 10.0 + # With --daemon, rclone exits quickly; check return code. + res = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) + if res.returncode != 0: + err = (res.stderr or res.stdout or "").strip() + raise RemoteMountError(f"rclone mount failed: {err or 'unknown error'}") + + # Wait briefly for mount to become usable. + # On some systems/FUSE setups the directory exists immediately but isn't connected yet. + deadline = time.time() + 15.0 + last_exc: Optional[Exception] = None while time.time() < deadline: try: - if os.path.isdir(mount_point) and (os.path.ismount(mount_point) or os.listdir(mount_point) is not None): - break - except Exception: - pass + # listdir will throw on common "not connected" errors + os.listdir(mount_point) + last_exc = None + break + except Exception as e: + last_exc = e time.sleep(0.25) + if last_exc is not None: + raise RemoteMountError(f"Timed out waiting for mount to become ready: {last_exc}") + return _MountedEndpoint(local_path=mount_point, mount_point=mount_point, proc=proc) @@ -773,8 +816,31 @@ def _log(msg: str, level: str = "info") -> None: if (self.direction or "").lower() == "bidirectional": if not bidirectional_func: raise NotImplementedError("Bidirectional engine not provided.") + + # IMPORTANT: + # The bidirectional engine historically enforced local endpoints only by checking + # endpoint types. Since remote endpoints are mounted to local paths for the duration + # of the run, we provide a local-view of this job to the engine. + job_local_view = Job( + id=self.id, + name=self.name, + enabled=self.enabled, + mode=self.mode, + direction=self.direction, + allowDeletion=self.allowDeletion, + preserveMetadata=self.preserveMetadata, + conflictPolicy=self.conflictPolicy, + pairId=self.pairId, + sourceEndpoint=Endpoint(type="local", location=src, auth={}), + targetEndpoint=Endpoint(type="local", location=dst, auth={}), + schedule=self.schedule, + lastRun=self.lastRun, + lastResult=self.lastResult, + lastError=self.lastError, + ) + _log(f"[Job] Running bidirectional job '{self.name}': {src} <-> {dst}", "info") - ok, stats = bidirectional_func(self, None) + ok, stats = bidirectional_func(job_local_view, None) else: if not copy_func: raise NotImplementedError("Copy function not provided.") diff --git a/src/replicator/replicator.py b/src/replicator/replicator.py index 794b14b..484e814 100644 --- a/src/replicator/replicator.py +++ b/src/replicator/replicator.py @@ -8,6 +8,13 @@ import os import json import shutil +import sys +import platform +import tempfile +import time +import subprocess +from pathlib import Path +from urllib.parse import urlparse # Add datetime import for lastRun/lastResult from datetime import datetime, timezone, timedelta @@ -31,6 +38,7 @@ from .migration import Migration from .job import Job, Endpoint, Schedule, JobStore + try: from core.helper import Helper from core.configuration import Configuration @@ -47,6 +55,258 @@ from database.sqlite import SQLite +# --------------------------------------------------------------------------- +# Remote endpoints (rclone mount) +# --------------------------------------------------------------------------- + +class RemoteMountError(RuntimeError): + pass + + +def _is_windows() -> bool: + return os.name == "nt" + + +def _find_rclone() -> str: + """Resolve rclone from PATH or bundled src/bin layout.""" + exe = "rclone.exe" if _is_windows() else "rclone" + + p = shutil.which(exe) + if p: + return p + + here = Path(__file__).resolve() + os_name = "windows" if _is_windows() else ("macos" if sys.platform == "darwin" else "linux") + + mach = (platform.machine() or "").lower() + if mach in {"x86_64", "amd64"}: + arch = "x86_64" + elif mach in {"aarch64", "arm64"}: + arch = "arm64" + elif mach.startswith("arm"): + arch = "arm" + elif mach in {"i386", "i686", "x86"}: + arch = "x86" + else: + arch = mach or "unknown" + + for parent in [here.parent] + list(here.parents): + candidate = parent / "src" / "bin" / "rclone" / os_name / arch / "bin" / exe + if candidate.exists() and candidate.is_file(): + return str(candidate) + + raise RemoteMountError("rclone not found (not in PATH and not bundled under src/bin/rclone).") + + +def _rclone_escape(val: str) -> str: + return str(val).replace("\\", "\\\\").replace(",", "\\,").replace(":", "\\:") + + +def _parse_smb_location(location: str) -> tuple[str, str, str]: + """Return (host, share, subpath) from location. + + Accepts forms: + - //host/share/subpath + - \\host\\share\\subpath + - host/share/subpath + """ + loc = (location or "").strip().replace("\\", "/") + loc = loc.lstrip("/") + parts = [p for p in loc.split("/") if p] + if len(parts) < 2: + raise RemoteMountError("SMB location must include host and share (e.g. //server/Share[/path]).") + host = parts[0] + share = parts[1] + subpath = "/".join(parts[2:]) if len(parts) > 2 else "" + return host, share, subpath + + +def _parse_host_path_location(location: str, default_scheme: str) -> tuple[str, str, str | None]: + """Parse host/path from either URL form or host:/path. + + Returns (host, path, user_from_location). + + Accepts: + - ftp://host/path + - sftp://user@host:22/path + - user@host:/path + - host:/path + """ + loc = (location or "").strip() + + if "://" in loc: + u = urlparse(loc) + host = u.hostname or "" + path = u.path or "/" + user = u.username + if not host: + raise RemoteMountError(f"Invalid {default_scheme} URL (missing host): {location}") + return host, path, user + + if ":" in loc: + left, right = loc.split(":", 1) + user = None + host = left + if "@" in left: + user, host = left.split("@", 1) + path = right if right.startswith("/") else ("/" + right) + if not host: + raise RemoteMountError(f"Invalid {default_scheme} location (missing host): {location}") + return host, path, user + + raise RemoteMountError( + f"Invalid {default_scheme} location. Use host:/path or {default_scheme}://host/path (got: {location})" + ) + + +def _build_rclone_remote(endpoint: dict[str, Any]) -> str: + """Build an on-the-fly rclone remote definition for mount.""" + t = (endpoint.get("type") or "local").lower() + loc = str(endpoint.get("location") or "") + auth = endpoint.get("auth") or {} + if not isinstance(auth, dict): + auth = {} + + if t == "smb": + host, share, subpath = _parse_smb_location(loc) + params: dict[str, str] = {"host": host, "share": share} + if subpath: + params["path"] = subpath + + guest = bool(auth.get("guest", True)) + username = str(auth.get("username") or "") + password = str(auth.get("password") or "") + domain = str(auth.get("domain") or "") + + if not guest and username: + params["user"] = username + if not guest and password: + params["pass"] = password + if domain: + params["domain"] = domain + + return ":smb," + ",".join(f"{k}={_rclone_escape(v)}" for k, v in params.items()) + ":" + + if t == "ftp": + host, path, user_from_loc = _parse_host_path_location(loc, "ftp") + params = {"host": host, "path": path} + try: + params["port"] = str(int(auth.get("port", endpoint.get("port") or 21))) + except Exception: + params["port"] = "21" + + guest = bool(auth.get("guest", False)) + username = str(auth.get("username") or user_from_loc or "") + password = str(auth.get("password") or "") + if not guest and username: + params["user"] = username + if not guest and password: + params["pass"] = password + + return ":ftp," + ",".join(f"{k}={_rclone_escape(v)}" for k, v in params.items()) + ":" + + if t == "ssh": + host, path, user_from_loc = _parse_host_path_location(loc, "sftp") + params = {"host": host, "path": path} + try: + params["port"] = str(int(auth.get("port", endpoint.get("port") or 22))) + except Exception: + params["port"] = "22" + + username = str(auth.get("username") or user_from_loc or "") + password = str(auth.get("password") or "") + use_key = bool(auth.get("useKey", False)) + key_path = str(auth.get("key") or auth.get("sshKey") or "") + + if username: + params["user"] = username + if (not use_key) and password: + params["pass"] = password + if use_key and key_path: + params["key_file"] = key_path + + return ":sftp," + ",".join(f"{k}={_rclone_escape(v)}" for k, v in params.items()) + ":" + + raise RemoteMountError(f"Unsupported remote endpoint type: {t}") + + +class _MountedEndpoint: + def __init__(self, local_path: str, mount_point: str | None = None, proc: subprocess.Popen | None = None): + self.local_path = local_path + self.mount_point = mount_point + self.proc = proc + + def cleanup(self) -> None: + if self.mount_point: + try: + rclone = _find_rclone() + subprocess.run([rclone, "unmount", self.mount_point], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) + except Exception: + pass + if self.proc: + try: + self.proc.terminate() + except Exception: + pass + + +def _mount_endpoint_if_remote(endpoint: dict[str, Any], job_id: int | None, role: str, logger: Any = None) -> _MountedEndpoint: + t = (endpoint.get("type") or "local").lower() + loc = str(endpoint.get("location") or "").strip() + if t == "local": + return _MountedEndpoint(local_path=loc) + + if t not in ("smb", "ftp", "ssh"): + raise RemoteMountError(f"Unsupported endpoint type: {t}") + + rclone = _find_rclone() + remote_def = _build_rclone_remote(endpoint) + + base = Path(tempfile.gettempdir()) / "replicator" / "mounts" / (str(job_id or "new")) / role + base.mkdir(parents=True, exist_ok=True) + mount_point = str(base) + + cmd: list[str] = [rclone, "mount", remote_def, mount_point] + + # On macOS/Linux, daemonize + if not _is_windows(): + cmd += ["--daemon"] + + extra_args = None + auth = endpoint.get("auth") + if isinstance(auth, dict): + extra_args = auth.get("rcloneArgs") + if isinstance(extra_args, list): + for a in extra_args: + if isinstance(a, str) and a.strip(): + cmd.append(a.strip()) + + if logger is not None and hasattr(logger, "append"): + try: + logger.append("[Replicator][rclone] " + " ".join(cmd), level="debug", channel="replicator") + except Exception: + pass + + proc: subprocess.Popen | None = None + if _is_windows(): + proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) + else: + subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) + + # Wait briefly for mount directory to become usable + deadline = time.time() + 10.0 + while time.time() < deadline: + try: + if os.path.isdir(mount_point): + # os.path.ismount can be unreliable on macOS FUSE; accept directory existence. + break + except Exception: + pass + time.sleep(0.25) + + return _MountedEndpoint(local_path=mount_point, mount_point=mount_point, proc=proc) + + class Replicator(QMainWindow): @@ -258,12 +518,10 @@ def _should_run_scheduled(self, job: Job, *, now_utc: datetime, now_local: datet # ------------------------------------------------------------------ def _endpoint_local_root(self, ep: Dict[str, Any]) -> str: - """Return the local root path for an endpoint or raise.""" + """Return the local root path for an endpoint or raise. + This is a simple extractor for the already-mounted path.""" if not isinstance(ep, dict): raise ValueError("Endpoint is missing") - typ = (ep.get("type") or "local").lower() - if typ != "local": - raise NotImplementedError(f"Bidirectional sync is currently implemented for local endpoints only (got '{typ}').") loc = (ep.get("location") or "").strip() if not loc: raise ValueError("Endpoint location is required") @@ -1240,7 +1498,31 @@ def _run_job(self, job: Job) -> bool: ok = False try: if str(direction).lower() == "bidirectional": - ok, bidi_stats = self._run_job_bidirectional(job.to_legacy_dict(), run_id) + # Mount remote endpoints (SMB/FTP/SSH) via rclone so the bidirectional engine can + # operate on local paths. + mounted: list[_MountedEndpoint] = [] + job_dict = job.to_legacy_dict() + + src_ep = job_dict.get("sourceEndpoint") or {"type": "local", "location": src, "auth": {}} + dst_ep = job_dict.get("targetEndpoint") or {"type": "local", "location": dst, "auth": {}} + + src_m = _mount_endpoint_if_remote(src_ep, job_id, "source", logger=self._logger) + mounted.append(src_m) + dst_m = _mount_endpoint_if_remote(dst_ep, job_id, "target", logger=self._logger) + mounted.append(dst_m) + + # Local view for the bidirectional sync engine + job_dict["sourceEndpoint"] = {"type": "local", "location": src_m.local_path, "auth": {}} + job_dict["targetEndpoint"] = {"type": "local", "location": dst_m.local_path, "auth": {}} + + try: + ok, bidi_stats = self._run_job_bidirectional(job_dict, run_id) + finally: + for m in reversed(mounted): + try: + m.cleanup() + except Exception: + pass else: ok = self._fs.copy( src, @@ -1251,6 +1533,9 @@ def _run_job(self, job: Job) -> bool: except NotImplementedError as e: self._log(f"[Replicator] Job '{name}' not supported: {e}", level="error") ok = False + except RemoteMountError as e: + self._log(f"[Replicator] Job '{name}' failed to mount remote endpoint: {e}", level="error") + ok = False except Exception as e: self._log(f"[Replicator] Job '{name}' failed: {e}", level="error") ok = False From d70b62aa560b4123d5719e5b5e84513489aea27e Mon Sep 17 00:00:00 2001 From: Louis Ouellet Date: Tue, 20 Jan 2026 10:49:03 -0500 Subject: [PATCH 39/51] BUGFIX: Attempting to debug and correct the process of transfering files through network shares. --- src/replicator/job.py | 208 +++++++++++++++++++++++++++--------------- 1 file changed, 132 insertions(+), 76 deletions(-) diff --git a/src/replicator/job.py b/src/replicator/job.py index 43f54f2..561b59a 100644 --- a/src/replicator/job.py +++ b/src/replicator/job.py @@ -19,6 +19,7 @@ import os import sys import platform +import re try: # corePY SQLite wrapper (preferred) @@ -26,11 +27,58 @@ except Exception: # pragma: no cover SQLite = None # type: ignore +try: + # corePY Share mount helper + from core.filesystem.share import Share, ShareAuth, ShareError # type: ignore +except Exception: # pragma: no cover + Share = None # type: ignore + ShareAuth = None # type: ignore + ShareError = Exception # type: ignore + JsonDict = Dict[str, Any] +# --------------------------------------------------------------------------- +# Logging helpers (Share adapter + secret redaction) +# --------------------------------------------------------------------------- + +def _redact_secrets(s: str) -> str: + """Redact obvious credentials from command strings/log lines.""" + if not s: + return s + # rclone on-the-fly remote: pass=..., user=... + s = re.sub(r"(pass=)([^,\s]+)", r"\1***", s, flags=re.IGNORECASE) + # common flags + s = re.sub(r"(--password\s+)(\S+)", r"\1***", s, flags=re.IGNORECASE) + s = re.sub(r"(--pass\s+)(\S+)", r"\1***", s, flags=re.IGNORECASE) + return s + + +class _ShareLogger: + """Adapter that exposes debug/info/warning/error and forwards to Job.run(logger=...).""" + + def __init__(self, fn: Optional[Callable[[str, str], None]]): + self._fn = fn + + def debug(self, msg: str) -> None: + if self._fn: + self._fn(_redact_secrets(msg), "debug") + + def info(self, msg: str) -> None: + if self._fn: + self._fn(_redact_secrets(msg), "info") + + def warning(self, msg: str) -> None: + if self._fn: + self._fn(_redact_secrets(msg), "warning") + + def error(self, msg: str) -> None: + if self._fn: + self._fn(_redact_secrets(msg), "error") + + # --------------------------------------------------------------------------- # Remote endpoints (rclone mount) # --------------------------------------------------------------------------- @@ -233,17 +281,20 @@ def _build_rclone_remote(endpoint: "Endpoint") -> Tuple[str, str]: class _MountedEndpoint: local_path: str mount_point: Optional[str] = None - proc: Optional[subprocess.Popen] = None + share: Any = None # Share instance when mounted via core.filesystem.share def cleanup(self) -> None: - # On Windows, the mount is typically held by the running rclone process. - if self.proc: + # Prefer Share.umount when available (it performs debug probing). + if self.share is not None and self.mount_point: try: - self.proc.terminate() + self.share.umount(self.mount_point, elevate=False) + return except Exception: + # fall through to best-effort cleanup pass if self.mount_point: + # Best-effort rclone unmount fallback (legacy) try: rclone = _find_rclone() subprocess.run( @@ -257,8 +308,18 @@ def cleanup(self) -> None: pass -def _mount_endpoint_if_remote(endpoint: "Endpoint", job_id: Union[int, None], role: str) -> _MountedEndpoint: - """If endpoint is remote (smb/ftp/ssh), mount it and return local path; otherwise return local location.""" +def _mount_endpoint_if_remote( + endpoint: "Endpoint", + job_id: Union[int, None], + role: str, + *, + logger: Optional[Callable[[str, str], None]] = None, + timeout: int = 15, +) -> _MountedEndpoint: + """If endpoint is remote (smb/ftp/ssh), mount it and return local path; otherwise return local location. + + Uses core.filesystem.share.Share (rclone-backed on macOS when only rclone is bundled). + """ t = (endpoint.type or "local").lower() if t == "local": return _MountedEndpoint(local_path=endpoint.location) @@ -266,82 +327,77 @@ def _mount_endpoint_if_remote(endpoint: "Endpoint", job_id: Union[int, None], ro if t not in ("smb", "ftp", "ssh"): raise RemoteMountError(f"Unsupported endpoint type: {t}") - rclone = _find_rclone() - remote_def, _ = _build_rclone_remote(endpoint) - - # Preflight: validate that rclone can list the remote. - # This catches bad credentials / unreachable host early and provides a useful error. - try: - pre = subprocess.run( - [rclone, "lsf", remote_def, "--max-depth", "1"], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - text=True, - timeout=20, - ) - if pre.returncode != 0: - err = (pre.stderr or pre.stdout or "").strip() - raise RemoteMountError(f"rclone preflight failed for {endpoint.type}: {err or 'unknown error'}") - except subprocess.TimeoutExpired: - raise RemoteMountError(f"rclone preflight timed out for {endpoint.type} endpoint") + if Share is None: + raise RemoteMountError("Share support not available (failed to import core.filesystem.share.Share)") # Mount under a stable temp folder for the job base = Path(tempfile.gettempdir()) / "replicator" / "mounts" / (str(job_id or "new")) / role base.mkdir(parents=True, exist_ok=True) mount_point = str(base) - cmd: List[str] = [rclone, "mount", remote_def, mount_point] - cmd += ["--ask-password", "false"] - - # Best-effort background on Unix. On Windows, we keep it in a subprocess. - if not _is_windows(): - cmd += ["--daemon"] - - # Some rclone builds may require --vfs-cache-mode for better behavior; allow user to set via auth/options - # If user provided extra rclone args in auth["rcloneArgs"] as a list, append them. - extra_args = endpoint.auth.get("rcloneArgs") if isinstance(endpoint.auth, dict) else None - if isinstance(extra_args, list): - for a in extra_args: - if isinstance(a, str) and a.strip(): - cmd.append(a.strip()) - - proc: Optional[subprocess.Popen] = None - if _is_windows(): - # Start in background; rclone mount will keep running. - proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) - # Give it a moment to fail fast if something is wrong. - time.sleep(0.5) - if proc.poll() is not None and proc.returncode not in (None, 0): - try: - err = (proc.stderr.read() if proc.stderr else "") - except Exception: - err = "" - raise RemoteMountError(f"rclone mount failed (windows): {(err or '').strip() or 'unknown error'}") - else: - # With --daemon, rclone exits quickly; check return code. - res = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) - if res.returncode != 0: - err = (res.stderr or res.stdout or "").strip() - raise RemoteMountError(f"rclone mount failed: {err or 'unknown error'}") - - # Wait briefly for mount to become usable. - # On some systems/FUSE setups the directory exists immediately but isn't connected yet. - deadline = time.time() + 15.0 - last_exc: Optional[Exception] = None - while time.time() < deadline: - try: - # listdir will throw on common "not connected" errors - os.listdir(mount_point) - last_exc = None - break - except Exception as e: - last_exc = e - time.sleep(0.25) + # Parse endpoint location into host + remote path + host = "" + remote = "" + auth_in = dict(endpoint.auth or {}) if isinstance(endpoint.auth, dict) else {} + + if t == "smb": + host, share, subpath = _parse_smb_location(endpoint.location) + remote = share + (f"/{subpath}" if subpath else "") + + elif t == "ftp": + host, path, user_from_loc = _parse_host_path_location(endpoint.location, "ftp") + remote = path + # If user not provided explicitly, accept user from URL/location + if user_from_loc and not auth_in.get("username"): + auth_in["username"] = user_from_loc - if last_exc is not None: - raise RemoteMountError(f"Timed out waiting for mount to become ready: {last_exc}") + elif t == "ssh": + host, path, user_from_loc = _parse_host_path_location(endpoint.location, "sftp") + remote = path + if user_from_loc and not auth_in.get("username"): + auth_in["username"] = user_from_loc + + # Build ShareAuth + try: + share_auth = ShareAuth( + username=str(auth_in.get("username") or ""), + password=str(auth_in.get("password") or ""), + domain=str(auth_in.get("domain") or ""), + port=int(auth_in.get("port") or (22 if t == "ssh" else 21 if t == "ftp" else 0)) or None, + private_key=str(auth_in.get("key") or ""), + guest=bool(auth_in.get("guest", True if t in ("smb",) else False)), + ) + except Exception: + # Fallback if ShareAuth signature changes + share_auth = None + + share_logger = _ShareLogger(logger) + share = Share(logger=share_logger) + + # Allow passing through extra rclone args (e.g. VFS cache mode) via auth['rcloneArgs'] + extra_args = auth_in.get("rcloneArgs") if isinstance(auth_in.get("rcloneArgs"), list) else None + if extra_args: + share_logger.debug(f"[Share] Extra mount args provided: {extra_args}") + + # Mount + try: + share_logger.debug(f"[Share] Mount request: protocol={t} host={host} remote={remote} mount_point={mount_point}") + # Share.mount supports a generic signature; pass known fields. + share.mount( + protocol=t, + host=host, + remote=remote, + mount_point=mount_point, + auth=share_auth or auth_in, + timeout=timeout, + read_only=False, + elevate=False, + ) + except Exception as e: + raise RemoteMountError(f"Failed to mount {t} endpoint via Share: {e}") - return _MountedEndpoint(local_path=mount_point, mount_point=mount_point, proc=proc) + # Share performs its own probe logging; return the mounted endpoint. + return _MountedEndpoint(local_path=mount_point, mount_point=mount_point, share=share) # --------------------------------------------------------------------------- @@ -801,9 +857,9 @@ def _log(msg: str, level: str = "info") -> None: # Resolve endpoints (mount remote endpoints to local paths via rclone) mounted: List[_MountedEndpoint] = [] - src_m = _mount_endpoint_if_remote(self.sourceEndpoint, self.id, "source") + src_m = _mount_endpoint_if_remote(self.sourceEndpoint, self.id, "source", logger=logger) mounted.append(src_m) - dst_m = _mount_endpoint_if_remote(self.targetEndpoint, self.id, "target") + dst_m = _mount_endpoint_if_remote(self.targetEndpoint, self.id, "target", logger=logger) mounted.append(dst_m) src = src_m.local_path From 92ae6a1d36f1113ecf04505d34cd0fed5e21eff0 Mon Sep 17 00:00:00 2001 From: Louis Ouellet Date: Tue, 20 Jan 2026 11:48:17 -0500 Subject: [PATCH 40/51] General: Updated corePY --- src/core | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core b/src/core index 0158f28..9f901cb 160000 --- a/src/core +++ b/src/core @@ -1 +1 @@ -Subproject commit 0158f28269fab3e61ff9e45397fc208c6c56195b +Subproject commit 9f901cbd73ede73d6d674b879023d92b9fa99a4c From 7d85934006c6cc8665da2df8c3559cace52d82ae Mon Sep 17 00:00:00 2001 From: Louis Ouellet Date: Tue, 20 Jan 2026 11:48:48 -0500 Subject: [PATCH 41/51] BUGFIX: Fixing the implementation of shares. --- src/replicator/job.py | 157 +-------------- src/replicator/replicator.py | 364 +++++++++++++++-------------------- 2 files changed, 162 insertions(+), 359 deletions(-) diff --git a/src/replicator/job.py b/src/replicator/job.py index 561b59a..ad1fa1b 100644 --- a/src/replicator/job.py +++ b/src/replicator/job.py @@ -11,14 +11,10 @@ import tempfile import time -import shutil -import subprocess from pathlib import Path from urllib.parse import urlparse import os -import sys -import platform import re try: @@ -79,56 +75,14 @@ def error(self, msg: str) -> None: self._fn(_redact_secrets(msg), "error") + # --------------------------------------------------------------------------- -# Remote endpoints (rclone mount) +# Remote endpoints (Share mount helpers) # --------------------------------------------------------------------------- class RemoteMountError(RuntimeError): pass - -def _is_windows() -> bool: - return (os.name == "nt") - - -def _find_rclone() -> str: - """Resolve rclone from PATH or bundled src/bin layout.""" - exe = "rclone.exe" if _is_windows() else "rclone" - - p = shutil.which(exe) - if p: - return p - - # Bundled layout expected by corePY-style projects: - # src/bin/rclone/[os]/[arch]/bin/rclone - here = Path(__file__).resolve() - os_name = "windows" if _is_windows() else ("macos" if sys.platform == "darwin" else "linux") - - mach = (platform.machine() or "").lower() - if mach in {"x86_64", "amd64"}: - arch = "x86_64" - elif mach in {"aarch64", "arm64"}: - arch = "arm64" - elif mach.startswith("arm"): - arch = "arm" - elif mach in {"i386", "i686", "x86"}: - arch = "x86" - else: - arch = mach or "unknown" - - for parent in [here.parent] + list(here.parents): - candidate = parent / "src" / "bin" / "rclone" / os_name / arch / "bin" / exe - if candidate.exists() and candidate.is_file(): - return str(candidate) - - raise RemoteMountError("rclone not found (not in PATH and not bundled under src/bin/rclone).") - - -def _rclone_escape(val: str) -> str: - # Escape separators used by rclone on-the-fly remotes. - return str(val).replace("\\", "\\\\").replace(",", "\\,").replace(":", "\\:") - - def _parse_smb_location(location: str) -> Tuple[str, str, str]: """Return (host, share, subpath) from location. @@ -148,7 +102,6 @@ def _parse_smb_location(location: str) -> Tuple[str, str, str]: subpath = "/".join(parts[2:]) if len(parts) > 2 else "" return host, share, subpath - def _parse_host_path_location(location: str, default_scheme: str) -> Tuple[str, str, Optional[str]]: """Parse host/path from either URL form or host:/path. @@ -188,95 +141,6 @@ def _parse_host_path_location(location: str, default_scheme: str) -> Tuple[str, f"Invalid {default_scheme} location. Use host:/path or {default_scheme}://host/path (got: {location})" ) - -def _build_rclone_remote(endpoint: "Endpoint") -> Tuple[str, str]: - """Return (remote_def, mount_subpath_description). - - mount_subpath_description is informational and currently unused. - """ - t = (endpoint.type or "local").lower() - auth = dict(endpoint.auth or {}) - - if t == "smb": - host, share, subpath = _parse_smb_location(endpoint.location) - params: Dict[str, str] = { - "host": host, - "share": share, - } - # Optional path within share - if subpath: - params["path"] = subpath - - # Credentials - username = str(auth.get("username") or "") - password = str(auth.get("password") or "") - domain = str(auth.get("domain") or "") - guest = bool(auth.get("guest", True)) - - if not guest and username: - params["user"] = username - if not guest and password: - params["pass"] = password - if domain: - params["domain"] = domain - - remote = ":smb," + ",".join(f"{k}={_rclone_escape(v)}" for k, v in params.items()) + ":" - return remote, subpath - - if t == "ftp": - host, path, user_from_loc = _parse_host_path_location(endpoint.location, "ftp") - params = { - "host": host, - "path": path, - } - try: - params["port"] = str(int(auth.get("port", 21))) - except Exception: - params["port"] = "21" - - username = str(auth.get("username") or user_from_loc or "") - password = str(auth.get("password") or "") - guest = bool(auth.get("guest", False)) - if not guest and username: - params["user"] = username - if not guest and password: - params["pass"] = password - - remote = ":ftp," + ",".join(f"{k}={_rclone_escape(v)}" for k, v in params.items()) + ":" - return remote, path - - if t == "ssh": - host, path, user_from_loc = _parse_host_path_location(endpoint.location, "sftp") - params = { - "host": host, - "path": path, - } - try: - params["port"] = str(int(auth.get("port", 22))) - except Exception: - params["port"] = "22" - - username = str(auth.get("username") or user_from_loc or "") - password = str(auth.get("password") or "") - use_key = bool(auth.get("useKey", False)) - key_path = str(auth.get("key") or "") - - if username: - params["user"] = username - if (not use_key) and password: - params["pass"] = password - if use_key and key_path: - # rclone sftp backend uses key_file - params["key_file"] = key_path - - remote = ":sftp," + ",".join(f"{k}={_rclone_escape(v)}" for k, v in params.items()) + ":" - return remote, path - - raise RemoteMountError(f"Unsupported remote endpoint type for mount: {endpoint.type}") - - -from dataclasses import dataclass - @dataclass class _MountedEndpoint: local_path: str @@ -284,26 +148,9 @@ class _MountedEndpoint: share: Any = None # Share instance when mounted via core.filesystem.share def cleanup(self) -> None: - # Prefer Share.umount when available (it performs debug probing). if self.share is not None and self.mount_point: try: self.share.umount(self.mount_point, elevate=False) - return - except Exception: - # fall through to best-effort cleanup - pass - - if self.mount_point: - # Best-effort rclone unmount fallback (legacy) - try: - rclone = _find_rclone() - subprocess.run( - [rclone, "unmount", self.mount_point], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - text=True, - timeout=15, - ) except Exception: pass diff --git a/src/replicator/replicator.py b/src/replicator/replicator.py index 484e814..033bf94 100644 --- a/src/replicator/replicator.py +++ b/src/replicator/replicator.py @@ -8,13 +8,9 @@ import os import json import shutil -import sys -import platform import tempfile import time -import subprocess from pathlib import Path -from urllib.parse import urlparse # Add datetime import for lastRun/lastResult from datetime import datetime, timezone, timedelta @@ -45,6 +41,7 @@ from core.log import Log from core.ui import MsgBox, Form from core.filesystem.filesystem import FileSystem + from core.filesystem.share import Share, ShareAuth from core.database.sqlite import SQLite except ImportError: from helper import Helper @@ -52,260 +49,216 @@ from log import Log from ui import MsgBox, Form from filesystem.filesystem import FileSystem + from filesystem.share import Share, ShareAuth from database.sqlite import SQLite + # --------------------------------------------------------------------------- -# Remote endpoints (rclone mount) +# Remote endpoints (Share mount) # --------------------------------------------------------------------------- class RemoteMountError(RuntimeError): pass -def _is_windows() -> bool: - return os.name == "nt" +class _ShareLogger: + """Adapter that forwards Share logs into Replicator's logger.""" + def __init__(self, append_fn): + self._append = append_fn -def _find_rclone() -> str: - """Resolve rclone from PATH or bundled src/bin layout.""" - exe = "rclone.exe" if _is_windows() else "rclone" + def debug(self, msg: str) -> None: + self._append(f"[Share] {msg}", level="debug") - p = shutil.which(exe) - if p: - return p + def info(self, msg: str) -> None: + self._append(f"[Share] {msg}", level="info") - here = Path(__file__).resolve() - os_name = "windows" if _is_windows() else ("macos" if sys.platform == "darwin" else "linux") + def warning(self, msg: str) -> None: + self._append(f"[Share] {msg}", level="warning") - mach = (platform.machine() or "").lower() - if mach in {"x86_64", "amd64"}: - arch = "x86_64" - elif mach in {"aarch64", "arm64"}: - arch = "arm64" - elif mach.startswith("arm"): - arch = "arm" - elif mach in {"i386", "i686", "x86"}: - arch = "x86" - else: - arch = mach or "unknown" + def error(self, msg: str) -> None: + self._append(f"[Share] {msg}", level="error") - for parent in [here.parent] + list(here.parents): - candidate = parent / "src" / "bin" / "rclone" / os_name / arch / "bin" / exe - if candidate.exists() and candidate.is_file(): - return str(candidate) - raise RemoteMountError("rclone not found (not in PATH and not bundled under src/bin/rclone).") +class _MountedEndpoint: + def __init__(self, local_path: str, mount_point: str | None = None, share: Share | None = None): + self.local_path = local_path + self.mount_point = mount_point + self.share = share + + def cleanup(self) -> None: + if self.share is not None and self.mount_point: + try: + self.share.umount(self.mount_point, elevate=False) + except Exception: + pass -def _rclone_escape(val: str) -> str: - return str(val).replace("\\", "\\\\").replace(",", "\\,").replace(":", "\\:") +# --- Location parsing helpers for remote endpoints --- -def _parse_smb_location(location: str) -> tuple[str, str, str]: - """Return (host, share, subpath) from location. +def _parse_smb_location(loc: str) -> tuple[str, str]: + """Parse SMB location into (host, remote). Accepts forms: - - //host/share/subpath - - \\host\\share\\subpath - - host/share/subpath + - \\host\Share + - \\host\Share\dir\sub + - //host/Share/dir + - host/Share/dir + Returns: + host, remote where remote is "Share" or "Share/dir/sub". """ - loc = (location or "").strip().replace("\\", "/") - loc = loc.lstrip("/") - parts = [p for p in loc.split("/") if p] + s = (loc or "").strip() + s = s.replace("/", "\\") + while s.startswith("\\"): + s = s[1:] + parts = [p for p in s.split("\\") if p] if len(parts) < 2: - raise RemoteMountError("SMB location must include host and share (e.g. //server/Share[/path]).") + raise RemoteMountError(f"Invalid SMB location: {loc}") host = parts[0] - share = parts[1] - subpath = "/".join(parts[2:]) if len(parts) > 2 else "" - return host, share, subpath + remote = "/".join(parts[1:]) + return host, remote -def _parse_host_path_location(location: str, default_scheme: str) -> tuple[str, str, str | None]: - """Parse host/path from either URL form or host:/path. +def _parse_host_path_location(loc: str) -> tuple[str, str]: + """Parse ssh/ftp-ish location into (host, remote_path). - Returns (host, path, user_from_location). - - Accepts: - - ftp://host/path - - sftp://user@host:22/path - - user@host:/path - - host:/path + Accepts forms: + - user@host:/path/to/dir + - host:/path/to/dir + - host/path/to/dir + - sftp://user@host/path (user is ignored here; auth provides it) + - ftp://user@host/path + + Returns: + host, remote_path (remote_path always starts with '/') """ - loc = (location or "").strip() - - if "://" in loc: - u = urlparse(loc) - host = u.hostname or "" - path = u.path or "/" - user = u.username - if not host: - raise RemoteMountError(f"Invalid {default_scheme} URL (missing host): {location}") - return host, path, user - - if ":" in loc: - left, right = loc.split(":", 1) - user = None - host = left - if "@" in left: - user, host = left.split("@", 1) - path = right if right.startswith("/") else ("/" + right) - if not host: - raise RemoteMountError(f"Invalid {default_scheme} location (missing host): {location}") - return host, path, user - - raise RemoteMountError( - f"Invalid {default_scheme} location. Use host:/path or {default_scheme}://host/path (got: {location})" - ) + s = (loc or "").strip() + if not s: + raise RemoteMountError("Empty location") - -def _build_rclone_remote(endpoint: dict[str, Any]) -> str: - """Build an on-the-fly rclone remote definition for mount.""" - t = (endpoint.get("type") or "local").lower() - loc = str(endpoint.get("location") or "") - auth = endpoint.get("auth") or {} - if not isinstance(auth, dict): - auth = {} - - if t == "smb": - host, share, subpath = _parse_smb_location(loc) - params: dict[str, str] = {"host": host, "share": share} - if subpath: - params["path"] = subpath - - guest = bool(auth.get("guest", True)) - username = str(auth.get("username") or "") - password = str(auth.get("password") or "") - domain = str(auth.get("domain") or "") - - if not guest and username: - params["user"] = username - if not guest and password: - params["pass"] = password - if domain: - params["domain"] = domain - - return ":smb," + ",".join(f"{k}={_rclone_escape(v)}" for k, v in params.items()) + ":" - - if t == "ftp": - host, path, user_from_loc = _parse_host_path_location(loc, "ftp") - params = {"host": host, "path": path} + # Strip scheme if present + if "://" in s: try: - params["port"] = str(int(auth.get("port", endpoint.get("port") or 21))) + scheme, rest = s.split("://", 1) + s = rest except Exception: - params["port"] = "21" + pass - guest = bool(auth.get("guest", False)) - username = str(auth.get("username") or user_from_loc or "") - password = str(auth.get("password") or "") - if not guest and username: - params["user"] = username - if not guest and password: - params["pass"] = password + # Drop user@ if provided (auth should carry username) + if "@" in s and not s.startswith("["): + # user@host:... or user@host/... + s = s.split("@", 1)[1] - return ":ftp," + ",".join(f"{k}={_rclone_escape(v)}" for k, v in params.items()) + ":" + host = "" + path = "" - if t == "ssh": - host, path, user_from_loc = _parse_host_path_location(loc, "sftp") - params = {"host": host, "path": path} - try: - params["port"] = str(int(auth.get("port", endpoint.get("port") or 22))) - except Exception: - params["port"] = "22" - - username = str(auth.get("username") or user_from_loc or "") - password = str(auth.get("password") or "") - use_key = bool(auth.get("useKey", False)) - key_path = str(auth.get("key") or auth.get("sshKey") or "") + if ":" in s: + host, path = s.split(":", 1) + elif "/" in s: + host, path = s.split("/", 1) + path = "/" + path + else: + host, path = s, "/" - if username: - params["user"] = username - if (not use_key) and password: - params["pass"] = password - if use_key and key_path: - params["key_file"] = key_path + host = host.strip() + path = (path or "/").strip() + if not path.startswith("/"): + path = "/" + path - return ":sftp," + ",".join(f"{k}={_rclone_escape(v)}" for k, v in params.items()) + ":" + if not host: + raise RemoteMountError(f"Invalid location: {loc}") - raise RemoteMountError(f"Unsupported remote endpoint type: {t}") + return host, path -class _MountedEndpoint: - def __init__(self, local_path: str, mount_point: str | None = None, proc: subprocess.Popen | None = None): - self.local_path = local_path - self.mount_point = mount_point - self.proc = proc +def _mount_endpoint_if_remote( + endpoint: dict[str, Any], + job_id: int | None, + role: str, + *, + share: Share, + log_append, +) -> _MountedEndpoint: + """Mount SMB/FTP/SSH endpoints using Share and return a local path usable by the sync engine.""" - def cleanup(self) -> None: - if self.mount_point: - try: - rclone = _find_rclone() - subprocess.run([rclone, "unmount", self.mount_point], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) - except Exception: - pass - if self.proc: - try: - self.proc.terminate() - except Exception: - pass - - -def _mount_endpoint_if_remote(endpoint: dict[str, Any], job_id: int | None, role: str, logger: Any = None) -> _MountedEndpoint: t = (endpoint.get("type") or "local").lower() loc = str(endpoint.get("location") or "").strip() + if t == "local": return _MountedEndpoint(local_path=loc) if t not in ("smb", "ftp", "ssh"): raise RemoteMountError(f"Unsupported endpoint type: {t}") - rclone = _find_rclone() - remote_def = _build_rclone_remote(endpoint) - base = Path(tempfile.gettempdir()) / "replicator" / "mounts" / (str(job_id or "new")) / role base.mkdir(parents=True, exist_ok=True) mount_point = str(base) - cmd: list[str] = [rclone, "mount", remote_def, mount_point] - - # On macOS/Linux, daemonize - if not _is_windows(): - cmd += ["--daemon"] - - extra_args = None - auth = endpoint.get("auth") - if isinstance(auth, dict): - extra_args = auth.get("rcloneArgs") - if isinstance(extra_args, list): - for a in extra_args: - if isinstance(a, str) and a.strip(): - cmd.append(a.strip()) - - if logger is not None and hasattr(logger, "append"): - try: - logger.append("[Replicator][rclone] " + " ".join(cmd), level="debug", channel="replicator") - except Exception: - pass + # Build auth/options payload (do NOT log secrets) + auth: dict[str, Any] = {} + if isinstance(endpoint.get("auth"), dict): + auth.update(endpoint.get("auth") or {}) + + # Backward-compatible: endpoints table columns may be stored at top-level + for k in ("port", "guest", "username", "password", "useKey", "sshKey", "options"): + if k in endpoint and endpoint.get(k) is not None and k not in auth: + auth[k] = endpoint.get(k) + + # Build ShareAuth object + share_auth = ShareAuth( + username=auth.get("username"), + password=auth.get("password"), + domain=auth.get("domain") or auth.get("workgroup"), + private_key=auth.get("sshKey") or auth.get("private_key"), + ) - proc: subprocess.Popen | None = None - if _is_windows(): - proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) + # Parse host and remote from location + if t == "smb": + host, remote = _parse_smb_location(loc) + elif t in ("ftp", "ssh"): + host, remote = _parse_host_path_location(loc) else: - subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) + raise RemoteMountError(f"Unsupported endpoint type: {t}") - # Wait briefly for mount directory to become usable - deadline = time.time() + 10.0 - while time.time() < deadline: - try: - if os.path.isdir(mount_point): - # os.path.ismount can be unreliable on macOS FUSE; accept directory existence. - break - except Exception: - pass - time.sleep(0.25) + # Determine port and options + port = None + try: + if auth.get("port") is not None: + port = int(auth.get("port")) + except Exception: + port = None + + opts = {} + if isinstance(auth.get("options"), dict): + opts.update(auth.get("options") or {}) + + # Emit a safe debug line (mask secrets, include host/remote) + safe_auth = dict(auth) + for k in ("password", "pass", "sshKey", "key", "key_file"): + if k in safe_auth and safe_auth[k]: + safe_auth[k] = "***" + log_append( + f"[Replicator][Share] mount protocol={t} host={host} remote={remote} mount_point={mount_point} auth={safe_auth}", + level="debug", + ) - return _MountedEndpoint(local_path=mount_point, mount_point=mount_point, proc=proc) + # Call Share.mount with correct signature + share.mount( + t, + host, + remote, + mount_point, + auth=share_auth, + port=port, + options=opts, + read_only=bool(auth.get("read_only") or auth.get("ro") or False), + elevate=False, + timeout=60, + ) + return _MountedEndpoint(local_path=mount_point, mount_point=mount_point, share=share) class Replicator(QMainWindow): @@ -1498,17 +1451,20 @@ def _run_job(self, job: Job) -> bool: ok = False try: if str(direction).lower() == "bidirectional": - # Mount remote endpoints (SMB/FTP/SSH) via rclone so the bidirectional engine can - # operate on local paths. + # Mount remote endpoints (SMB/FTP/SSH) via Share so the bidirectional engine can + # operate on local filesystem paths AND we get proper connectivity debug logs. mounted: list[_MountedEndpoint] = [] job_dict = job.to_legacy_dict() src_ep = job_dict.get("sourceEndpoint") or {"type": "local", "location": src, "auth": {}} dst_ep = job_dict.get("targetEndpoint") or {"type": "local", "location": dst, "auth": {}} - src_m = _mount_endpoint_if_remote(src_ep, job_id, "source", logger=self._logger) + share_logger = _ShareLogger(self._log) + share = Share(logger=share_logger) + + src_m = _mount_endpoint_if_remote(src_ep, job_id, "source", share=share, log_append=self._log) mounted.append(src_m) - dst_m = _mount_endpoint_if_remote(dst_ep, job_id, "target", logger=self._logger) + dst_m = _mount_endpoint_if_remote(dst_ep, job_id, "target", share=share, log_append=self._log) mounted.append(dst_m) # Local view for the bidirectional sync engine @@ -1620,7 +1576,7 @@ def _db_debug_snapshot(self, verbose: bool = False) -> None: self._log(f"[Replicator][DB] {t}: latest {len(rows)} row(s)", level="debug") for r in rows: dr = dict(r) - for k in ("password", "sshKey"): + for k in ("password", "pass", "sshKey", "key_file"): if k in dr and dr[k]: dr[k] = "***" self._log(f"[Replicator][DB] {t}: {dr}", level="debug") From adc3d6a9b9dc06c2ac7a5d4b6b812c1d1982bbc1 Mon Sep 17 00:00:00 2001 From: Louis Ouellet Date: Tue, 20 Jan 2026 12:00:57 -0500 Subject: [PATCH 42/51] General: Updated corePY --- src/core | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core b/src/core index 9f901cb..223d1f8 160000 --- a/src/core +++ b/src/core @@ -1 +1 @@ -Subproject commit 9f901cbd73ede73d6d674b879023d92b9fa99a4c +Subproject commit 223d1f8b1618b9f886ede7c3f91e9ef3f7f6c80e From 8930ddccf0a66b99aae5743d25e88b5472d70a92 Mon Sep 17 00:00:00 2001 From: Louis Ouellet Date: Tue, 20 Jan 2026 12:04:28 -0500 Subject: [PATCH 43/51] General: Updated corePY --- src/core | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core b/src/core index 223d1f8..40c164a 160000 --- a/src/core +++ b/src/core @@ -1 +1 @@ -Subproject commit 223d1f8b1618b9f886ede7c3f91e9ef3f7f6c80e +Subproject commit 40c164a7c18da18296e61611860721dfdd205f8d From 54d0afd997b30eeb3568533bb79deecdab5b7f35 Mon Sep 17 00:00:00 2001 From: Louis Ouellet Date: Tue, 20 Jan 2026 12:16:18 -0500 Subject: [PATCH 44/51] General: Updated corePY --- src/core | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core b/src/core index 40c164a..3761b2d 160000 --- a/src/core +++ b/src/core @@ -1 +1 @@ -Subproject commit 40c164a7c18da18296e61611860721dfdd205f8d +Subproject commit 3761b2dfe447cfd9f306f6224cbb962beb10f79c From 85cc2455626837a6fa0e44454909b3080ca0f150 Mon Sep 17 00:00:00 2001 From: Louis Ouellet Date: Tue, 20 Jan 2026 12:38:39 -0500 Subject: [PATCH 45/51] General: Updated corePY --- src/core | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core b/src/core index 3761b2d..3ff05e8 160000 --- a/src/core +++ b/src/core @@ -1 +1 @@ -Subproject commit 3761b2dfe447cfd9f306f6224cbb962beb10f79c +Subproject commit 3ff05e839641a94bec9ec99f48c1d8d380b22f32 From 2458d0958f60c29f97d650fdfe83fb2ca13848e9 Mon Sep 17 00:00:00 2001 From: Louis Ouellet Date: Tue, 20 Jan 2026 12:39:08 -0500 Subject: [PATCH 46/51] General: Added the bundled rclone. --- src/bin/rclone | 1 + 1 file changed, 1 insertion(+) create mode 120000 src/bin/rclone diff --git a/src/bin/rclone b/src/bin/rclone new file mode 120000 index 0000000..276b3ba --- /dev/null +++ b/src/bin/rclone @@ -0,0 +1 @@ +../core/bin/rclone \ No newline at end of file From 729151e8c85c05306e6f01907bd0ec87abafb0ea Mon Sep 17 00:00:00 2001 From: Louis Ouellet Date: Tue, 20 Jan 2026 12:43:53 -0500 Subject: [PATCH 47/51] General: Updated corePY --- src/core | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core b/src/core index 3ff05e8..41fb8d7 160000 --- a/src/core +++ b/src/core @@ -1 +1 @@ -Subproject commit 3ff05e839641a94bec9ec99f48c1d8d380b22f32 +Subproject commit 41fb8d7b44ee2078f62f28dcf45b321c3462a728 From 79a26241585dcdcff357e65b726e81e763ba84f1 Mon Sep 17 00:00:00 2001 From: Louis Ouellet Date: Tue, 20 Jan 2026 12:54:03 -0500 Subject: [PATCH 48/51] General: Updated corePY --- src/core | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core b/src/core index 41fb8d7..76cfa2b 160000 --- a/src/core +++ b/src/core @@ -1 +1 @@ -Subproject commit 41fb8d7b44ee2078f62f28dcf45b321c3462a728 +Subproject commit 76cfa2b9a0ff82b23ad7d42de9664e76de21340c From 57f4136aecb8dad72f2fe95d22f918cc98a17704 Mon Sep 17 00:00:00 2001 From: Louis Ouellet Date: Tue, 20 Jan 2026 13:28:08 -0500 Subject: [PATCH 49/51] General: Updated corePY --- src/core | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core b/src/core index 76cfa2b..4e2ad24 160000 --- a/src/core +++ b/src/core @@ -1 +1 @@ -Subproject commit 76cfa2b9a0ff82b23ad7d42de9664e76de21340c +Subproject commit 4e2ad24fd643103251d1a33290226ae5a7096601 From 8799902c25155e8c49e3df580f136ba446819fbf Mon Sep 17 00:00:00 2001 From: Louis Ouellet Date: Tue, 20 Jan 2026 14:10:30 -0500 Subject: [PATCH 50/51] General: Code Clean up --- src/replicator/job.py | 105 +++----------------- src/replicator/replicator.py | 69 ++----------- src/replicator/ui.py | 187 +++++++---------------------------- 3 files changed, 56 insertions(+), 305 deletions(-) diff --git a/src/replicator/job.py b/src/replicator/job.py index ad1fa1b..94c6f82 100644 --- a/src/replicator/job.py +++ b/src/replicator/job.py @@ -44,8 +44,8 @@ def _redact_secrets(s: str) -> str: """Redact obvious credentials from command strings/log lines.""" if not s: return s - # rclone on-the-fly remote: pass=..., user=... s = re.sub(r"(pass=)([^,\s]+)", r"\1***", s, flags=re.IGNORECASE) + s = re.sub(r"(password=)([^,\s]+)", r"\1***", s, flags=re.IGNORECASE) # common flags s = re.sub(r"(--password\s+)(\S+)", r"\1***", s, flags=re.IGNORECASE) s = re.sub(r"(--pass\s+)(\S+)", r"\1***", s, flags=re.IGNORECASE) @@ -102,44 +102,6 @@ def _parse_smb_location(location: str) -> Tuple[str, str, str]: subpath = "/".join(parts[2:]) if len(parts) > 2 else "" return host, share, subpath -def _parse_host_path_location(location: str, default_scheme: str) -> Tuple[str, str, Optional[str]]: - """Parse host/path from either URL form or host:/path. - - Returns (host, path, user_from_location). - - Accepts: - - ftp://host/path - - sftp://user@host:22/path - - user@host:/path - - host:/path - """ - loc = (location or "").strip() - - # URL form - if "://" in loc: - u = urlparse(loc) - host = u.hostname or "" - path = u.path or "/" - user = u.username - if not host: - raise RemoteMountError(f"Invalid {default_scheme} URL (missing host): {location}") - return host, path, user - - # scp-ish form - if ":" in loc: - left, right = loc.split(":", 1) - user = None - host = left - if "@" in left: - user, host = left.split("@", 1) - path = right if right.startswith("/") else ("/" + right) - if not host: - raise RemoteMountError(f"Invalid {default_scheme} location (missing host): {location}") - return host, path, user - - raise RemoteMountError( - f"Invalid {default_scheme} location. Use host:/path or {default_scheme}://host/path (got: {location})" - ) @dataclass class _MountedEndpoint: @@ -163,7 +125,7 @@ def _mount_endpoint_if_remote( logger: Optional[Callable[[str, str], None]] = None, timeout: int = 15, ) -> _MountedEndpoint: - """If endpoint is remote (smb/ftp/ssh), mount it and return local path; otherwise return local location. + """If endpoint is remote (smb), mount it and return local path; otherwise return local location. Uses core.filesystem.share.Share (rclone-backed on macOS when only rclone is bundled). """ @@ -171,7 +133,7 @@ def _mount_endpoint_if_remote( if t == "local": return _MountedEndpoint(local_path=endpoint.location) - if t not in ("smb", "ftp", "ssh"): + if t != "smb": raise RemoteMountError(f"Unsupported endpoint type: {t}") if Share is None: @@ -191,28 +153,15 @@ def _mount_endpoint_if_remote( host, share, subpath = _parse_smb_location(endpoint.location) remote = share + (f"/{subpath}" if subpath else "") - elif t == "ftp": - host, path, user_from_loc = _parse_host_path_location(endpoint.location, "ftp") - remote = path - # If user not provided explicitly, accept user from URL/location - if user_from_loc and not auth_in.get("username"): - auth_in["username"] = user_from_loc - - elif t == "ssh": - host, path, user_from_loc = _parse_host_path_location(endpoint.location, "sftp") - remote = path - if user_from_loc and not auth_in.get("username"): - auth_in["username"] = user_from_loc - # Build ShareAuth try: share_auth = ShareAuth( username=str(auth_in.get("username") or ""), password=str(auth_in.get("password") or ""), domain=str(auth_in.get("domain") or ""), - port=int(auth_in.get("port") or (22 if t == "ssh" else 21 if t == "ftp" else 0)) or None, + port=int(auth_in.get("port") or 445) or None, private_key=str(auth_in.get("key") or ""), - guest=bool(auth_in.get("guest", True if t in ("smb",) else False)), + guest=bool(auth_in.get("guest", True)), ) except Exception: # Fallback if ShareAuth signature changes @@ -265,7 +214,7 @@ def validate(self, role: str) -> List[str]: errs.append(f"{role} endpoint location is required.") t = (self.type or "").lower() - if t in ("ftp", "ssh"): + if t == "smb": port = self.auth.get("port") if port is not None: try: @@ -290,34 +239,17 @@ def to_db_fields(self, role: str) -> JsonDict: if t == "local": pass - elif t in ("smb", "ftp"): + elif t == "smb": guest = 1 if bool(auth.get("guest", True)) else 0 username = auth.get("username") or None password = auth.get("password") or None - if t == "ftp": - try: - port = int(auth.get("port", 21)) - except Exception: - port = 21 # Optional SMB domain - if t == "smb": - options["domain"] = auth.get("domain") + options["domain"] = auth.get("domain") # Optional rclone args (applies to all remote types) if isinstance(auth.get("rcloneArgs"), list): options["rcloneArgs"] = auth.get("rcloneArgs") - elif t == "ssh": - useKey = 1 if bool(auth.get("useKey", False)) else 0 - username = auth.get("username") or None - password = auth.get("password") or None - try: - port = int(auth.get("port", 22)) - except Exception: - port = 22 - sshKey = auth.get("key") or None - if isinstance(auth.get("rcloneArgs"), list): - options["rcloneArgs"] = auth.get("rcloneArgs") - known_keys = {"guest", "username", "password", "port", "useKey", "key"} + known_keys = {"guest", "username", "password", "port", "domain"} for k, v in auth.items(): if k not in known_keys: options[k] = v @@ -342,25 +274,14 @@ def from_db_row(row: Mapping[str, Any]) -> "Endpoint": if t == "local": auth = {} - elif t in ("smb", "ftp"): + elif t == "smb": auth = { "guest": bool(row.get("guest", 1)), "username": row.get("username") or "", "password": row.get("password") or "", } - if t == "ftp": - auth["port"] = row.get("port") or 21 - if t == "smb": - # domain is stored in options JSON when present - pass - elif t == "ssh": - auth = { - "useKey": bool(row.get("useKey", 0)), - "username": row.get("username") or "", - "password": row.get("password") or "", - "port": row.get("port") or 22, - "key": row.get("sshKey") or "", - } + auth["port"] = row.get("port") or 445 + # domain is stored in options JSON when present opt = row.get("options") if opt: @@ -702,7 +623,7 @@ def _log(msg: str, level: str = "info") -> None: preserve = bool(self.preserveMetadata) allow_del = bool(self.allowDeletion) - # Resolve endpoints (mount remote endpoints to local paths via rclone) + # Resolve endpoints (mount SMB endpoints to local paths for the duration of the run) mounted: List[_MountedEndpoint] = [] src_m = _mount_endpoint_if_remote(self.sourceEndpoint, self.id, "source", logger=logger) mounted.append(src_m) diff --git a/src/replicator/replicator.py b/src/replicator/replicator.py index 033bf94..280ab7e 100644 --- a/src/replicator/replicator.py +++ b/src/replicator/replicator.py @@ -121,56 +121,6 @@ def _parse_smb_location(loc: str) -> tuple[str, str]: return host, remote -def _parse_host_path_location(loc: str) -> tuple[str, str]: - """Parse ssh/ftp-ish location into (host, remote_path). - - Accepts forms: - - user@host:/path/to/dir - - host:/path/to/dir - - host/path/to/dir - - sftp://user@host/path (user is ignored here; auth provides it) - - ftp://user@host/path - - Returns: - host, remote_path (remote_path always starts with '/') - """ - s = (loc or "").strip() - if not s: - raise RemoteMountError("Empty location") - - # Strip scheme if present - if "://" in s: - try: - scheme, rest = s.split("://", 1) - s = rest - except Exception: - pass - - # Drop user@ if provided (auth should carry username) - if "@" in s and not s.startswith("["): - # user@host:... or user@host/... - s = s.split("@", 1)[1] - - host = "" - path = "" - - if ":" in s: - host, path = s.split(":", 1) - elif "/" in s: - host, path = s.split("/", 1) - path = "/" + path - else: - host, path = s, "/" - - host = host.strip() - path = (path or "/").strip() - if not path.startswith("/"): - path = "/" + path - - if not host: - raise RemoteMountError(f"Invalid location: {loc}") - - return host, path def _mount_endpoint_if_remote( @@ -181,7 +131,7 @@ def _mount_endpoint_if_remote( share: Share, log_append, ) -> _MountedEndpoint: - """Mount SMB/FTP/SSH endpoints using Share and return a local path usable by the sync engine.""" + """Mount SMB endpoints using Share and return a local path usable by the sync engine.""" t = (endpoint.get("type") or "local").lower() loc = str(endpoint.get("location") or "").strip() @@ -189,7 +139,7 @@ def _mount_endpoint_if_remote( if t == "local": return _MountedEndpoint(local_path=loc) - if t not in ("smb", "ftp", "ssh"): + if t != "smb": raise RemoteMountError(f"Unsupported endpoint type: {t}") base = Path(tempfile.gettempdir()) / "replicator" / "mounts" / (str(job_id or "new")) / role @@ -214,13 +164,8 @@ def _mount_endpoint_if_remote( private_key=auth.get("sshKey") or auth.get("private_key"), ) - # Parse host and remote from location - if t == "smb": - host, remote = _parse_smb_location(loc) - elif t in ("ftp", "ssh"): - host, remote = _parse_host_path_location(loc) - else: - raise RemoteMountError(f"Unsupported endpoint type: {t}") + # Parse host and remote from location (SMB only) + host, remote = _parse_smb_location(loc) # Determine port and options port = None @@ -229,6 +174,8 @@ def _mount_endpoint_if_remote( port = int(auth.get("port")) except Exception: port = None + if port is None: + port = 445 opts = {} if isinstance(auth.get("options"), dict): @@ -236,7 +183,7 @@ def _mount_endpoint_if_remote( # Emit a safe debug line (mask secrets, include host/remote) safe_auth = dict(auth) - for k in ("password", "pass", "sshKey", "key", "key_file"): + for k in ("password", "pass"): if k in safe_auth and safe_auth[k]: safe_auth[k] = "***" log_append( @@ -1451,7 +1398,7 @@ def _run_job(self, job: Job) -> bool: ok = False try: if str(direction).lower() == "bidirectional": - # Mount remote endpoints (SMB/FTP/SSH) via Share so the bidirectional engine can + # Mount remote endpoints (SMB only) via Share so the bidirectional engine can # operate on local filesystem paths AND we get proper connectivity debug logs. mounted: list[_MountedEndpoint] = [] job_dict = job.to_legacy_dict() diff --git a/src/replicator/ui.py b/src/replicator/ui.py index f0e5c71..9457cab 100644 --- a/src/replicator/ui.py +++ b/src/replicator/ui.py @@ -287,10 +287,12 @@ def _open_schedule(self) -> None: def _build_endpoint(self, existing: Dict[str, Any]): type_combo = QComboBox() - type_combo.addItems(["local", "smb", "ftp", "ssh"]) + type_combo.addItems(["local", "smb"]) idx = type_combo.findText(existing.get("type", "local")) if idx >= 0: type_combo.setCurrentIndex(idx) + if idx < 0: + type_combo.setCurrentIndex(0) type_combo.setFixedWidth(110) type_combo.setProperty("existing_type", existing.get("type", "local")) type_combo.setProperty("existing_auth", existing.get("auth", {}) or {}) @@ -300,15 +302,9 @@ def _build_endpoint(self, existing: Dict[str, Any]): port_spin = QSpinBox() port_spin.setRange(1, 65535) port_spin.setFixedWidth(110) - - existing_type = existing.get("type", "local") - existing_auth = existing.get("auth", {}) or {} - if existing_type == "ftp": - port_spin.setValue(int(existing_auth.get("port", 21) or 21)) - elif existing_type == "ssh": - port_spin.setValue(int(existing_auth.get("port", 22) or 22)) - else: - port_spin.setValue(21) + # Ports are not used for local/SMB endpoints. + port_spin.setValue(1) + port_spin.setVisible(False) endpoint_fields = QWidget() fields_lay = QHBoxLayout(endpoint_fields) @@ -394,84 +390,17 @@ def _smb_guest_update(): smb_guest.stateChanged.connect(_smb_guest_update) _smb_guest_update() - # FTP auth - ftp_wrap = QWidget() - ftp_wrap.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Maximum) - ftp_form = QFormLayout(ftp_wrap) - ftp_form.setContentsMargins(0, 0, 0, 0) - ftp_guest = QCheckBox("Login as Guest") - ftp_user_lbl = QLabel("Username") - ftp_username = QLineEdit() - ftp_pass_lbl = QLabel("Password") - ftp_password = QLineEdit() - ftp_password.setEchoMode(QLineEdit.Password) - - ftp_auth = existing.get("auth", {}) if existing.get("type") == "ftp" else {} - ftp_guest.setChecked(bool(ftp_auth.get("guest", True))) - ftp_username.setText(ftp_auth.get("username", "")) - ftp_password.setText(ftp_auth.get("password", "")) - - ftp_form.addRow(ftp_guest) - ftp_form.addRow(ftp_user_lbl, ftp_username) - ftp_form.addRow(ftp_pass_lbl, ftp_password) - - def _ftp_guest_update(): - guest = ftp_guest.isChecked() - ftp_user_lbl.setVisible(not guest) - ftp_username.setVisible(not guest) - ftp_pass_lbl.setVisible(not guest) - ftp_password.setVisible(not guest) - - ftp_guest.stateChanged.connect(_ftp_guest_update) - _ftp_guest_update() - - # SSH auth - ssh_wrap = QWidget() - ssh_wrap.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Maximum) - ssh_form = QFormLayout(ssh_wrap) - ssh_form.setContentsMargins(0, 0, 0, 0) - ssh_use_key = QCheckBox("Use SSH Key") - ssh_user_lbl = QLabel("Username") - ssh_username = QLineEdit() - ssh_pass_lbl = QLabel("Password") - ssh_password = QLineEdit() - ssh_password.setEchoMode(QLineEdit.Password) - ssh_key_lbl = QLabel("SSH Key") - ssh_key_text = QTextEdit() - ssh_key_text.setPlaceholderText("Paste SSH private key here...") - ssh_key_text.setFixedHeight(110) - - ssh_auth = existing.get("auth", {}) if existing.get("type") == "ssh" else {} - ssh_username.setText(ssh_auth.get("username", "")) - ssh_password.setText(ssh_auth.get("password", "")) - ssh_key_text.setPlainText(ssh_auth.get("key", "")) - ssh_use_key.setChecked(bool(ssh_auth.get("useKey", True if not ssh_password.text().strip() else False))) - - ssh_form.addRow(ssh_use_key) - ssh_form.addRow(ssh_user_lbl, ssh_username) - ssh_form.addRow(ssh_pass_lbl, ssh_password) - ssh_form.addRow(ssh_key_lbl, ssh_key_text) - - def _ssh_use_key_update(): - use_key = ssh_use_key.isChecked() - ssh_user_lbl.setVisible(True) - ssh_username.setVisible(True) - ssh_pass_lbl.setVisible(not use_key) - ssh_password.setVisible(not use_key) - ssh_key_lbl.setVisible(use_key) - ssh_key_text.setVisible(use_key) - - ssh_use_key.stateChanged.connect(_ssh_use_key_update) - _ssh_use_key_update() - auth_lay.addWidget(auth_title) auth_lay.addWidget(smb_wrap) - auth_lay.addWidget(ftp_wrap) - auth_lay.addWidget(ssh_wrap) - widgets["smb"] = {"wrap": smb_wrap, "guest": smb_guest, "user_lbl": smb_user_lbl, "username": smb_username, "pass_lbl": smb_pass_lbl, "password": smb_password} - widgets["ftp"] = {"wrap": ftp_wrap, "guest": ftp_guest, "user_lbl": ftp_user_lbl, "username": ftp_username, "pass_lbl": ftp_pass_lbl, "password": ftp_password} - widgets["ssh"] = {"wrap": ssh_wrap, "useKey": ssh_use_key, "user_lbl": ssh_user_lbl, "username": ssh_username, "pass_lbl": ssh_pass_lbl, "password": ssh_password, "key_lbl": ssh_key_lbl, "key": ssh_key_text} + widgets["smb"] = { + "wrap": smb_wrap, + "guest": smb_guest, + "user_lbl": smb_user_lbl, + "username": smb_username, + "pass_lbl": smb_pass_lbl, + "password": smb_password, + } return type_combo, location_edit, port_spin, endpoint_row, auth_widget, widgets @@ -489,34 +418,14 @@ def _on_type_changed( placeholder = { "local": "Local path (e.g. /data or C:\\Data)", "smb": "SMB path (e.g. \\\\SERVER\\Share\\Folder)", - "ftp": "FTP host/path (e.g. ftp.example.com:/folder)", - "ssh": "SSH host/path (e.g. example.com:/folder)", }.get(typ, "") location_edit.setPlaceholderText(placeholder) # For local endpoints, clicking the location field opens a folder chooser. # For non-local endpoints, keep normal typing behavior. if (typ or "").lower() == "local": location_edit.setCursorPosition(len(location_edit.text())) - - if typ in ("ftp", "ssh"): - port_spin.setVisible(True) - - existing_type = type_combo.property("existing_type") or "local" - existing_auth = type_combo.property("existing_auth") or {} - - if initial and existing_type == typ: - if typ == "ftp": - port_spin.setValue(int(existing_auth.get("port", 21) or 21)) - else: - port_spin.setValue(int(existing_auth.get("port", 22) or 22)) - else: - cur = int(port_spin.value()) - if typ == "ftp" and cur in (0, 22): - port_spin.setValue(21) - elif typ == "ssh" and cur in (0, 21): - port_spin.setValue(22) - else: - port_spin.setVisible(False) + # Ports are not used for local/SMB endpoints. + port_spin.setVisible(False) if typ == "local": auth_widget.setVisible(False) @@ -525,15 +434,15 @@ def _on_type_changed( auth_widget.setVisible(True) auth_widget.setMaximumHeight(16777215) - for key in ("smb", "ftp", "ssh"): + # Hide all auth sections + for key in ("smb",): if key in widgets and "wrap" in widgets[key]: widgets[key]["wrap"].setVisible(False) - if typ in ("smb", "ftp", "ssh"): - widgets[typ]["wrap"].setVisible(True) - - if typ in ("smb", "ftp"): - w = widgets[typ] + # Show only the relevant auth section + if typ == "smb": + widgets["smb"]["wrap"].setVisible(True) + w = widgets["smb"] if w["guest"].isChecked() is False: if not w["username"].text().strip() and not w["password"].text().strip(): w["guest"].setChecked(True) @@ -565,47 +474,19 @@ def _on_ok(self): MsgBox.show(self, "Job", "Target location is required.", icon="warning") return - if src_type in ("smb", "ftp"): - w = self._source_auth_widgets[src_type] + if src_type == "smb": + w = self._source_auth_widgets["smb"] if not w["guest"].isChecked(): if not w["username"].text().strip() or not w["password"].text().strip(): MsgBox.show(self, "Job", "Source username and password are required.", icon="warning") return - elif src_type == "ssh": - w = self._source_auth_widgets["ssh"] - if not w["username"].text().strip(): - MsgBox.show(self, "Job", "Source SSH username is required.", icon="warning") - return - use_key = w["useKey"].isChecked() - if use_key: - if not w["key"].toPlainText().strip(): - MsgBox.show(self, "Job", "Source SSH key is required when 'Use SSH Key' is enabled.", icon="warning") - return - else: - if not w["password"].text().strip(): - MsgBox.show(self, "Job", "Source SSH password is required when not using a key.", icon="warning") - return - if tgt_type in ("smb", "ftp"): - w = self._target_auth_widgets[tgt_type] + if tgt_type == "smb": + w = self._target_auth_widgets["smb"] if not w["guest"].isChecked(): if not w["username"].text().strip() or not w["password"].text().strip(): MsgBox.show(self, "Job", "Target username and password are required.", icon="warning") return - elif tgt_type == "ssh": - w = self._target_auth_widgets["ssh"] - if not w["username"].text().strip(): - MsgBox.show(self, "Job", "Target SSH username is required.", icon="warning") - return - use_key = w["useKey"].isChecked() - if use_key: - if not w["key"].toPlainText().strip(): - MsgBox.show(self, "Job", "Target SSH key is required when 'Use SSH Key' is enabled.", icon="warning") - return - else: - if not w["password"].text().strip(): - MsgBox.show(self, "Job", "Target SSH password is required when not using a key.", icon="warning") - return self.accept() @@ -623,13 +504,15 @@ def _extract(type_combo: QComboBox, location_edit: QLineEdit, port_spin: QSpinBo auth = {} elif typ == "smb": w = widgets["smb"] - auth = {"guest": bool(w["guest"].isChecked()), "username": w["username"].text().strip(), "password": w["password"].text()} - elif typ == "ftp": - w = widgets["ftp"] - auth = {"guest": bool(w["guest"].isChecked()), "username": w["username"].text().strip(), "password": w["password"].text(), "port": int(port_spin.value())} - elif typ == "ssh": - w = widgets["ssh"] - auth = {"useKey": bool(w["useKey"].isChecked()), "username": w["username"].text().strip(), "password": w["password"].text(), "port": int(port_spin.value()), "key": w["key"].toPlainText()} + auth = { + "guest": bool(w["guest"].isChecked()), + "username": w["username"].text().strip(), + "password": w["password"].text(), + } + else: + # Unsupported/legacy types fall back to local + typ = "local" + auth = {} return {"type": typ, "location": location, "auth": auth} From 5c328bbc208112b660ac0bc7d17aebae31b3bd68 Mon Sep 17 00:00:00 2001 From: Louis Ouellet Date: Tue, 20 Jan 2026 14:18:00 -0500 Subject: [PATCH 51/51] General: Code Clean up --- README.md | 6 +++--- src/replicator/job.py | 1 - src/replicator/replicator.py | 1 - 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index d7f920e..add0fce 100644 --- a/README.md +++ b/README.md @@ -31,9 +31,9 @@ Contributions to Replicator are welcome! If you have ideas for new features or h ## To Do - ~~**Support for Local**: Add support for Local filesystems.~~ - - **Support for SMB**: Add support for SMB Shares. - - **Support for FTP**: Add support for FTP Shares. - - **Support for SSHFS**: Add support for SSH filesystems. + - ~~**Support for SMB**: Add support for SMB Shares.~~ + - ~~**Support for FTP**: Add support for FTP Shares.~~ --- REMOVED --- + - ~~**Support for SSHFS**: Add support for SSH filesystems.~~ --- REMOVED --- - ~~**Mode Mirror**: Add replication mode Mirror.~~ - **Mode Incremental**: Add replication mode Incremental. - ~~**Direction**: Add replication direction (One way, Two way).~~ diff --git a/src/replicator/job.py b/src/replicator/job.py index 94c6f82..c7ae25c 100644 --- a/src/replicator/job.py +++ b/src/replicator/job.py @@ -160,7 +160,6 @@ def _mount_endpoint_if_remote( password=str(auth_in.get("password") or ""), domain=str(auth_in.get("domain") or ""), port=int(auth_in.get("port") or 445) or None, - private_key=str(auth_in.get("key") or ""), guest=bool(auth_in.get("guest", True)), ) except Exception: diff --git a/src/replicator/replicator.py b/src/replicator/replicator.py index 280ab7e..96680a1 100644 --- a/src/replicator/replicator.py +++ b/src/replicator/replicator.py @@ -161,7 +161,6 @@ def _mount_endpoint_if_remote( username=auth.get("username"), password=auth.get("password"), domain=auth.get("domain") or auth.get("workgroup"), - private_key=auth.get("sshKey") or auth.get("private_key"), ) # Parse host and remote from location (SMB only)