├── requirements.txt ├── .gitignore ├── screenshots ├── overview.png ├── offline_device.png └── pending_device_and_folder.png ├── .dockerignore ├── cron ├── hourly │ └── retry_offline_devices └── daily │ └── report ├── Dockerfile ├── env_reader.py ├── .github └── workflows │ └── build_image.yml ├── README.MD ├── app.py ├── syncthing_multi_server ├── syncthing.py └── syncthing_muxer.py ├── templates └── index.html └── LICENSE /requirements.txt: -------------------------------------------------------------------------------- 1 | flask 2 | requests 3 | gunicorn -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .venv/ 2 | __pycache__/ 3 | device_list.json 4 | docker-compose.yml 5 | sm2.log -------------------------------------------------------------------------------- /screenshots/overview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nware-lab/sm2/HEAD/screenshots/overview.png -------------------------------------------------------------------------------- /screenshots/offline_device.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nware-lab/sm2/HEAD/screenshots/offline_device.png -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | **/.gitea/ 2 | **/.venv/ 3 | **/__pycache__/ 4 | /screenshots 5 | device_list.json 6 | docker-compose.yml 7 | sm2.log -------------------------------------------------------------------------------- /screenshots/pending_device_and_folder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nware-lab/sm2/HEAD/screenshots/pending_device_and_folder.png -------------------------------------------------------------------------------- /cron/hourly/retry_offline_devices: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # retry offline devices 4 | wget http://127.0.0.1:8456/retry_offline_devices -O /dev/null -------------------------------------------------------------------------------- /cron/daily/report: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | #report to home 3 | #It looks like here we are ignoring if the user has configured whether or to do the reporting. 4 | #The users decision is captured by the webserver. 5 | #Future goal is to remove this cron job when the reporting is disable by the user 6 | echo doing the report call 7 | wget http://127.0.0.1:8456/report -O /dev/null -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:alpine3.21 2 | 3 | ENV PYTHONUNBUFFERED=true 4 | 5 | COPY requirements.txt . 6 | RUN pip3 install --no-cache-dir -r requirements.txt 7 | ENV SM2_VERSION=0.1.7 8 | ENV TZ=UTC 9 | COPY . /app 10 | COPY ./cron /etc/periodic 11 | RUN chmod +x -R /etc/periodic/ 12 | WORKDIR /app 13 | ENTRYPOINT ["python3"] 14 | HEALTHCHECK --interval=5s --timeout=1s --start-period=5s --retries=1 CMD ps aux | grep 'gunicorn' || exit 1 15 | CMD ["/usr/local/bin/gunicorn","-w", "1","-t", "5", "-b","0.0.0.0:8456","--timeout","10","app:app"] 16 | -------------------------------------------------------------------------------- /env_reader.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | 4 | def get_devices_from_env_vriables(): 5 | print("checking env variables for device configs") 6 | count = 1 7 | running = True 8 | devices = list() 9 | while (running): 10 | 11 | name= os.environ.get(f"DEV_NAME_{count}", None) 12 | if name == None: 13 | break 14 | url= os.environ.get(f"DEV_URL_{count}", None) 15 | api_key= os.environ.get(f"DEV_API_KEY_{count}", None) 16 | # checking if all keys are present is done when devices are added to the mux 17 | 18 | # got all the vars for this round to lets add them 19 | devices.append({"name": name, "url": url, "api_key": api_key}) 20 | 21 | count += 1 22 | 23 | return devices 24 | -------------------------------------------------------------------------------- /.github/workflows/build_image.yml: -------------------------------------------------------------------------------- 1 | name: Build sm2 docker image 2 | on: 3 | push: 4 | 5 | jobs: 6 | push_to_registry: 7 | name: Push Docker image to Docker Hub 8 | runs-on: ubuntu-latest 9 | permissions: 10 | packages: write 11 | contents: read 12 | attestations: write 13 | id-token: write 14 | steps: 15 | - name: Check out the repo 16 | uses: actions/checkout@v4 17 | 18 | - name: Log in to Docker Hub 19 | uses: docker/login-action@v3 20 | with: 21 | username: ${{ secrets.DOCKER_HUB_USERNAME }} 22 | password: ${{ secrets.DOCKER_HUB_TOKEN }} 23 | - name: Build and push Docker image for main branch => latest 24 | id: push_latest_image 25 | uses: docker/build-push-action@3b5e8027fcad23fda98b2e3ac259d8d67585f671 26 | with: 27 | context: . 28 | file: ./Dockerfile 29 | push: true 30 | tags: nwarelab/sm2:latest 31 | if: github.ref_name == 'main' 32 | 33 | 34 | - name: Build and push Docker image for other branches 35 | id: push_branch_image 36 | uses: docker/build-push-action@3b5e8027fcad23fda98b2e3ac259d8d67585f671 37 | with: 38 | context: . 39 | file: ./Dockerfile 40 | push: true 41 | tags: nwarelab/sm2:${{github.ref_name}} 42 | if: github.ref_name != 'main' 43 | 44 | -------------------------------------------------------------------------------- /README.MD: -------------------------------------------------------------------------------- 1 | # sm² 2 | Syncthing Multi Server Monitor 3 | stylized as sm² (or sm2 for convenience) 4 | 5 | 6 | ## The goal: 7 | Monitor multiple syncthing instances while using as little diskspace(docker image size) & memory while doing it. 8 | 9 | This affected the following parts: 10 | - no use of an already made syncthing library (this also allows sm2 to only ever call GET endpoinds) 11 | - no fancy ui -> lower diskspace, loadtime ( I also could not make a fancy ui if I tried) 12 | - no logging exept printing to console logging increased the ram usage by ~50% 13 | 14 | 15 | This project was made with very little sleep so there will be typo's, missed conventions, ... let me know when you see anything. 16 | Any feedback is appreciated. 17 | 18 | # features 19 | - Sync status of each device (threaded for performance) 20 | - List of folders with local changes (if there are any) 21 | - List pending devices or folders (if there are any) 22 | - Display of errors (if there are any) 23 | - API for monitoring (I use changedetecion.io) 24 | - Small footprint: docker image ~65MB, ram usage 34MB (goal is to lower this back down) 25 | - Responsive UI that works on mobile devices 26 | - Configure devices with environment variables or with a json file 27 | - handling devices going offline and returning (phones, laptops, ...) 28 | 29 | # Screenshots 30 | 31 |
33 |
36 |
37 |