Migrate to Poetry packaging and restructure codebase #21

Merged
Sudo-Ivan merged 23 commits from codebase-improvements into master 2025-12-01 18:24:25 +00:00
128 changed files with 2292 additions and 17499 deletions

View File

@@ -3,24 +3,36 @@ README.md
LICENSE
donate.md
screenshots/
docs/
# Development files
.github/
electron/
scripts/
Makefile
# Build artifacts and cache
build/
dist/
public/
meshchatx/public/
node_modules/
__pycache__/
*.pyc
*.pyo
*.pyd
*.py[cod]
*$py.class
*.so
.Python
*.egg-info/
*.egg
python-dist/
# Virtual environments
env/
venv/
ENV/
env.bak/
venv.bak/
.venv/
# IDE and editor files
.vscode/
@@ -47,9 +59,19 @@ Dockerfile*
docker-compose*.yml
.dockerignore
# Local storage and runtime data
storage/
testing/
telemetry_test_lxmf/
# Logs
*.log
# Temporary files
*.tmp
*.temp
# Environment variables
.env
.env.local
.env.*.local

View File

@@ -27,96 +27,14 @@ on:
default: 'true'
type: boolean
permissions:
contents: read
jobs:
build_windows:
runs-on: windows-latest
if: github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && github.event.inputs.build_windows == 'true')
permissions:
contents: write
steps:
- name: Clone Repo
uses: actions/checkout@50fbc622fc4ef5163becd7fab6573eac35f8462e # v1
- name: Install NodeJS
uses: actions/setup-node@f1f314fca9dfce2769ece7d933488f076716723e # v1
with:
node-version: 22
- name: Install Python
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
with:
python-version: "3.12"
- name: Install Python Deps
run: |
python -m venv venv
venv\Scripts\pip install --upgrade pip
venv\Scripts\pip install -r requirements.txt
- name: Install NodeJS Deps
run: npm install
- name: Build Electron App
run: npm run dist
- name: Create Release
id: create_release
uses: ncipollo/release-action@b7eabc95ff50cbeeedec83973935c8f306dfcd0b # v1
with:
draft: true
allowUpdates: true
replacesArtifacts: true
omitDraftDuringUpdate: true
omitNameDuringUpdate: true
artifacts: "dist/*-win-installer.exe,dist/*-win-portable.exe"
build_mac:
runs-on: macos-13
if: github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && github.event.inputs.build_mac == 'true')
permissions:
contents: write
steps:
- name: Clone Repo
uses: actions/checkout@50fbc622fc4ef5163becd7fab6573eac35f8462e # v1
- name: Install NodeJS
uses: actions/setup-node@f1f314fca9dfce2769ece7d933488f076716723e # v1
with:
node-version: 18
- name: Install Python
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
with:
python-version: "3.11"
- name: Install Python Deps
run: |
python3 -m venv venv
venv/bin/pip install --upgrade pip
venv/bin/pip install -r requirements.txt
- name: Install NodeJS Deps
run: npm install
- name: Build Electron App
run: npm run dist
- name: Create Release
id: create_release
uses: ncipollo/release-action@b7eabc95ff50cbeeedec83973935c8f306dfcd0b # v1
with:
draft: true
allowUpdates: true
replacesArtifacts: true
omitDraftDuringUpdate: true
omitNameDuringUpdate: true
artifacts: "dist/*-mac.dmg"
build_linux:
build_frontend:
runs-on: ubuntu-latest
if: github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && github.event.inputs.build_linux == 'true')
permissions:
contents: write
contents: read
steps:
- name: Clone Repo
uses: actions/checkout@50fbc622fc4ef5163becd7fab6573eac35f8462e # v1
@@ -131,31 +49,201 @@ jobs:
with:
coderabbitai[bot] commented 2025-12-01 06:13:57 +00:00 (Migrated from github.com)
Review

⚠️ Potential issue | 🔴 Critical

Fix shell syntax error in SHA256 checksum generation script.

The for loop at line 226 has invalid shell syntax: 2>/dev/null cannot be placed after the glob pattern list in a for statement. This causes shellcheck to fail parsing (SC1058, SC1072, SC1073). Since the conditional if [ -f "$file" ] already handles non-matching globs, the redirection is unnecessary.

Apply this diff to fix the syntax error:

      - name: Generate SHA256 checksums
        run: |
          cd release-assets
          echo "## SHA256 Checksums" > release-body.md
          echo "" >> release-body.md
          
-         for file in *.exe *.dmg *.AppImage *.deb *.whl 2>/dev/null; do
+         for file in *.exe *.dmg *.AppImage *.deb *.whl; do
            if [ -f "$file" ]; then
              sha256sum "$file" | tee "${file}.sha256"
              echo "\`$(cat "${file}.sha256")\`" >> release-body.md
            fi
          done
          
          echo "" >> release-body.md
          echo "Individual \`.sha256\` files are included for each artifact." >> release-body.md
          
          cat release-body.md
          echo ""
          echo "Generated .sha256 files:"
-         ls -1 *.sha256 2>/dev/null || echo "No .sha256 files found"
+         ls -1 *.sha256 || echo "No .sha256 files found"
🧰 Tools
🪛 actionlint (1.7.9)

221-221: shellcheck reported issue in this script: SC1058:error:5:49: Expected 'do'

(shellcheck)


221-221: shellcheck reported issue in this script: SC1072:error:5:50: Fix any mentioned problems and try again

(shellcheck)


221-221: shellcheck reported issue in this script: SC1073:error:5:1: Couldn't parse this for loop. Fix to allow more checks

(shellcheck)

🤖 Prompt for AI Agents
In .github/workflows/build.yml around lines 220 to 239, the for loop at line
~226 includes an invalid redirection after the glob list which breaks shell
parsing; remove the trailing `2>/dev/null` from the for statement so the loop
reads: for file in *.exe *.dmg *.AppImage *.deb *.whl; do — keep the existing
`if [ -f "$file" ]` check to ignore non-matching globs and leave other
redirections (e.g. the final `ls -1 *.sha256 2>/dev/null`) untouched.
_⚠️ Potential issue_ | _🔴 Critical_ **Fix shell syntax error in SHA256 checksum generation script.** The for loop at line 226 has invalid shell syntax: `2>/dev/null` cannot be placed after the glob pattern list in a for statement. This causes shellcheck to fail parsing (SC1058, SC1072, SC1073). Since the conditional `if [ -f "$file" ]` already handles non-matching globs, the redirection is unnecessary. Apply this diff to fix the syntax error: ```diff - name: Generate SHA256 checksums run: | cd release-assets echo "## SHA256 Checksums" > release-body.md echo "" >> release-body.md - for file in *.exe *.dmg *.AppImage *.deb *.whl 2>/dev/null; do + for file in *.exe *.dmg *.AppImage *.deb *.whl; do if [ -f "$file" ]; then sha256sum "$file" | tee "${file}.sha256" echo "\`$(cat "${file}.sha256")\`" >> release-body.md fi done echo "" >> release-body.md echo "Individual \`.sha256\` files are included for each artifact." >> release-body.md cat release-body.md echo "" echo "Generated .sha256 files:" - ls -1 *.sha256 2>/dev/null || echo "No .sha256 files found" + ls -1 *.sha256 || echo "No .sha256 files found" ``` <details> <summary>🧰 Tools</summary> <details> <summary>🪛 actionlint (1.7.9)</summary> 221-221: shellcheck reported issue in this script: SC1058:error:5:49: Expected 'do' (shellcheck) --- 221-221: shellcheck reported issue in this script: SC1072:error:5:50: Fix any mentioned problems and try again (shellcheck) --- 221-221: shellcheck reported issue in this script: SC1073:error:5:1: Couldn't parse this for loop. Fix to allow more checks (shellcheck) </details> </details> <details> <summary>🤖 Prompt for AI Agents</summary> ``` In .github/workflows/build.yml around lines 220 to 239, the for loop at line ~226 includes an invalid redirection after the glob list which breaks shell parsing; remove the trailing `2>/dev/null` from the for statement so the loop reads: for file in *.exe *.dmg *.AppImage *.deb *.whl; do — keep the existing `if [ -f "$file" ]` check to ignore non-matching globs and leave other redirections (e.g. the final `ls -1 *.sha256 2>/dev/null`) untouched. ``` </details> <!-- This is an auto-generated comment by CodeRabbit -->
python-version: "3.12"
- name: Sync versions
run: python scripts/sync_version.py
- name: Install NodeJS Deps
run: npm install
- name: Build Frontend
run: npm run build-frontend
- name: Upload frontend artifact
uses: actions/upload-artifact@v4
with:
name: frontend-build
path: meshchatx/public
if-no-files-found: error
build_desktop:
name: Build Desktop (${{ matrix.name }})
needs: build_frontend
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- name: windows
os: windows-latest
node: 22
python: "3.12"
release_artifacts: "dist/*-win-installer.exe,dist/*-win-portable.exe"
build_input: build_windows
- name: mac
os: macos-13
node: 18
python: "3.11"
release_artifacts: "dist/*-mac.dmg"
build_input: build_mac
- name: linux
os: ubuntu-latest
node: 22
python: "3.12"
release_artifacts: "dist/*-linux.AppImage,dist/*-linux.deb,python-dist/*.whl"
build_input: build_linux
permissions:
contents: write
steps:
- name: Clone Repo
if: |
github.event_name == 'push' ||
(github.event_name == 'workflow_dispatch' && inputs[matrix.build_input] == true)
uses: actions/checkout@50fbc622fc4ef5163becd7fab6573eac35f8462e # v1
- name: Install NodeJS
if: |
github.event_name == 'push' ||
(github.event_name == 'workflow_dispatch' && inputs[matrix.build_input] == true)
uses: actions/setup-node@f1f314fca9dfce2769ece7d933488f076716723e # v1
with:
node-version: ${{ matrix.node }}
- name: Install Python
if: |
github.event_name == 'push' ||
(github.event_name == 'workflow_dispatch' && inputs[matrix.build_input] == true)
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
with:
python-version: ${{ matrix.python }}
- name: Install Poetry
if: |
github.event_name == 'push' ||
(github.event_name == 'workflow_dispatch' && inputs[matrix.build_input] == true)
run: python -m pip install --upgrade pip poetry
- name: Sync versions
if: |
github.event_name == 'push' ||
(github.event_name == 'workflow_dispatch' && inputs[matrix.build_input] == true)
run: python scripts/sync_version.py
- name: Install Python Deps
if: |
github.event_name == 'push' ||
(github.event_name == 'workflow_dispatch' && inputs[matrix.build_input] == true)
run: python -m poetry install
- name: Install NodeJS Deps
if: |
github.event_name == 'push' ||
(github.event_name == 'workflow_dispatch' && inputs[matrix.build_input] == true)
run: npm install
- name: Prepare frontend directory
if: |
github.event_name == 'push' ||
(github.event_name == 'workflow_dispatch' && inputs[matrix.build_input] == true)
run: python scripts/prepare_frontend_dir.py
- name: Download frontend artifact
if: |
github.event_name == 'push' ||
(github.event_name == 'workflow_dispatch' && inputs[matrix.build_input] == true)
uses: actions/download-artifact@v4
with:
name: frontend-build
path: meshchatx/public
- name: Install patchelf
if: |
matrix.name == 'linux' &&
(github.event_name == 'push' ||
(github.event_name == 'workflow_dispatch' && inputs[matrix.build_input] == true))
run: sudo apt-get update && sudo apt-get install -y patchelf
- name: Install Python Deps
- name: Build Python wheel
if: |
matrix.name == 'linux' &&
(github.event_name == 'push' ||
(github.event_name == 'workflow_dispatch' && inputs[matrix.build_input] == true))
run: |
python3 -m venv venv
venv/bin/pip install --upgrade pip
venv/bin/pip install -r requirements.txt
- name: Install NodeJS Deps
run: npm install
python -m poetry build -f wheel
mkdir -p python-dist
mv dist/*.whl python-dist/
rm -rf dist
- name: Build Electron App
run: npm run dist
if: |
github.event_name == 'push' ||
(github.event_name == 'workflow_dispatch' && inputs[matrix.build_input] == true)
run: npm run dist-prebuilt
- name: Upload build artifacts
if: |
github.event_name == 'push' ||
(github.event_name == 'workflow_dispatch' && inputs[matrix.build_input] == true)
uses: actions/upload-artifact@v4
with:
name: build-${{ matrix.name }}
path: |
dist/*-win-installer.exe
dist/*-win-portable.exe
dist/*-mac.dmg
dist/*-linux.AppImage
dist/*-linux.deb
python-dist/*.whl
if-no-files-found: ignore
create_release:
name: Create Release
needs: build_desktop
runs-on: ubuntu-latest
if: github.event_name == 'push'
permissions:
contents: write
steps:
- name: Download all artifacts
uses: actions/download-artifact@v4
with:
path: artifacts
- name: Display structure of downloaded files
run: ls -R artifacts
- name: Prepare release assets
run: |
mkdir -p release-assets
find artifacts -type f \( -name "*.exe" -o -name "*.dmg" -o -name "*.AppImage" -o -name "*.deb" -o -name "*.whl" \) -exec cp {} release-assets/ \;
ls -lh release-assets/
- name: Generate SHA256 checksums
run: |
cd release-assets
echo "## SHA256 Checksums" > release-body.md
echo "" >> release-body.md
for file in *.exe *.dmg *.AppImage *.deb *.whl; do
if [ -f "$file" ]; then
sha256sum "$file" | tee "${file}.sha256"
echo "\`$(cat "${file}.sha256")\`" >> release-body.md
fi
done
echo "" >> release-body.md
echo "Individual \`.sha256\` files are included for each artifact." >> release-body.md
cat release-body.md
echo ""
echo "Generated .sha256 files:"
ls -1 *.sha256 2>/dev/null || echo "No .sha256 files found"
- name: Create Release
id: create_release
uses: ncipollo/release-action@b7eabc95ff50cbeeedec83973935c8f306dfcd0b # v1
with:
draft: true
allowUpdates: true
replacesArtifacts: true
omitDraftDuringUpdate: true
omitNameDuringUpdate: true
artifacts: "dist/*-linux.AppImage,dist/*-linux.deb"
artifacts: "release-assets/*"
bodyFile: "release-assets/release-body.md"
build_docker:
runs-on: ubuntu-latest

54
.gitignore vendored
View File

@@ -1,13 +1,57 @@
# IDE and editor files
.idea
node_modules
.vscode/
*.swp
*.swo
*~
# build files
# Dependencies
node_modules/
# Python
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
*.egg-info/
dist/
*.egg
# Virtual environments
venv/
env/
ENV/
env.bak/
venv.bak/
.venv/
# Build files
/build/
/dist/
/public/
/meshchatx/public/
public/
/electron/build/exe/
python-dist/
# local storage
# Local storage and runtime data
storage/
testing/
telemetry_test_lxmf/
*.pyc
# Logs
*.log
# OS files
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db
# Environment variables
.env
.env.local
.env.*.local

View File

@@ -10,9 +10,8 @@ FROM node:${NODE_VERSION}-alpine@${NODE_ALPINE_SHA256} AS build-frontend
WORKDIR /src
# Copy required source files
COPY *.json .
COPY *.js .
COPY src/frontend ./src/frontend
COPY package*.json vite.config.js ./
COPY meshchatx ./meshchatx
# Install NodeJS deps, exluding electron
RUN npm install --omit=dev && \
@@ -34,12 +33,10 @@ RUN apk add --no-cache --virtual .build-deps \
apk del .build-deps
# Copy prebuilt frontend
COPY --from=build-frontend /src/public public
COPY --from=build-frontend /src/meshchatx/public meshchatx/public
# Copy other required source files
COPY *.py .
COPY src/__init__.py ./src/__init__.py
COPY src/backend ./src/backend
COPY *.json .
COPY meshchatx ./meshchatx
COPY pyproject.toml poetry.lock ./
CMD ["python", "meshchat.py", "--host=0.0.0.0", "--reticulum-config-dir=/config/.reticulum", "--storage-dir=/config/.meshchat", "--headless"]
CMD ["python", "-m", "meshchatx.meshchat", "--host=0.0.0.0", "--reticulum-config-dir=/config/.reticulum", "--storage-dir=/config/.meshchat", "--headless"]

View File

@@ -1,26 +1,39 @@
.PHONY: install run clean build build-appimage build-exe dist
.PHONY: install run develop clean build build-appimage build-exe dist sync-version wheel node_modules python build-docker run-docker
VENV = venv
PYTHON = $(VENV)/bin/python
PIP = $(VENV)/bin/pip
PYTHON ?= python
POETRY = $(PYTHON) -m poetry
NPM = npm
install: $(VENV) node_modules
DOCKER_COMPOSE_CMD ?= docker compose
DOCKER_COMPOSE_FILE ?= docker-compose.yml
DOCKER_IMAGE ?= reticulum-meshchatx:local
DOCKER_BUILDER ?= meshchatx-builder
DOCKER_PLATFORMS ?= linux/amd64
DOCKER_BUILD_FLAGS ?= --load
DOCKER_BUILD_ARGS ?=
DOCKER_CONTEXT ?= .
DOCKERFILE ?= Dockerfile
$(VENV):
python3 -m venv $(VENV)
$(PIP) install --upgrade pip
$(PIP) install -r requirements.txt
install: sync-version node_modules python
node_modules:
$(NPM) install
python:
$(POETRY) install
run: install
$(PYTHON) meshchat.py
$(POETRY) run meshchat
develop: run
build: install
$(NPM) run build
wheel: install
$(POETRY) build -f wheel
$(PYTHON) scripts/move_wheels.py
build-appimage: build
$(NPM) run electron-postinstall
$(NPM) run dist -- --linux AppImage
@@ -32,10 +45,28 @@ build-exe: build
dist: build-appimage
clean:
rm -rf $(VENV)
rm -rf node_modules
rm -rf build
rm -rf dist
rm -rf python-dist
rm -rf meshchatx/public
sync-version:
$(PYTHON) scripts/sync_version.py
build-docker:
@if ! docker buildx inspect $(DOCKER_BUILDER) >/dev/null 2>&1; then \
docker buildx create --name $(DOCKER_BUILDER) --use >/dev/null; \
else \
docker buildx use $(DOCKER_BUILDER); \
fi
docker buildx build --builder $(DOCKER_BUILDER) --platform $(DOCKER_PLATFORMS) \
$(DOCKER_BUILD_FLAGS) \
-t $(DOCKER_IMAGE) \
$(DOCKER_BUILD_ARGS) \
-f $(DOCKERFILE) \
$(DOCKER_CONTEXT)
run-docker:
MESHCHAT_IMAGE="$(DOCKER_IMAGE)" \
$(DOCKER_COMPOSE_CMD) -f $(DOCKER_COMPOSE_FILE) up --remove-orphans --pull never reticulum-meshchatx

View File

@@ -8,22 +8,22 @@ A heavily customized fork of [Reticulum MeshChat](https://github.com/liamcottle/
- [x] Ability to set inbound and propagation node stamps.
coderabbitai[bot] commented 2025-12-01 06:13:58 +00:00 (Migrated from github.com)
Review

⚠️ Potential issue | 🟡 Minor

Minor style issue: "etc" should be "etc."

In American English, the abbreviation requires a period.

-- [ ] More tools (translate, LoRa calculator, LXMFy bots, etc)
+- [ ] More tools (translate, LoRa calculator, LXMFy bots, etc.)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

- [ ] More tools (translate, LoRa calculator, LXMFy bots, etc.)
🧰 Tools
🪛 LanguageTool

[style] ~16-~16: In American English, abbreviations like “etc.” require a period.
Context: ...translate, LoRa calculator, LXMFy bots, etc) - [x] Codebase reorganization and clea...

(ETC_PERIOD)

🤖 Prompt for AI Agents
In README.md around line 16, the list item uses "etc" without the required
period; update the line to use "etc." (i.e., change "etc" to "etc.") to follow
American English abbreviation style.

Addressed in commits ac839df to c98131f

_⚠️ Potential issue_ | _🟡 Minor_ **Minor style issue: "etc" should be "etc."** In American English, the abbreviation requires a period. ```diff -- [ ] More tools (translate, LoRa calculator, LXMFy bots, etc) +- [ ] More tools (translate, LoRa calculator, LXMFy bots, etc.) ``` <!-- suggestion_start --> <details> <summary>📝 Committable suggestion</summary> > ‼️ **IMPORTANT** > Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements. ```suggestion - [ ] More tools (translate, LoRa calculator, LXMFy bots, etc.) ``` </details> <!-- suggestion_end --> <details> <summary>🧰 Tools</summary> <details> <summary>🪛 LanguageTool</summary> [style] ~16-~16: In American English, abbreviations like “etc.” require a period. Context: ...translate, LoRa calculator, LXMFy bots, etc) - [x] Codebase reorganization and clea... (ETC_PERIOD) </details> </details> <details> <summary>🤖 Prompt for AI Agents</summary> ``` In README.md around line 16, the list item uses "etc" without the required period; update the line to use "etc." (i.e., change "etc" to "etc.") to follow American English abbreviation style. ``` </details> <!-- fingerprinting:phantom:medusa:ocelot --> <!-- This is an auto-generated comment by CodeRabbit --> ✅ Addressed in commits ac839df to c98131f
- [x] Better config parsing.
- [x] Cancel page fetching or file downloads
- [x] Block recieving messages from users.
- [x] Block receiving messages from users.
- [ ] Spam filter (based on keywords)
- [ ] Multi-identity support.
- [ ] Multi-language support
- [ ] Offline Reticulum documentation tool
- [ ] More tools (translate, LoRa calculator, LXMFy bots, etc)
- [ ] Codebase reorginization and cleanup.
- [ ] More tools (translate, LoRa calculator, LXMFy bots, etc.)
- [x] Codebase reorganization and cleanup.
- [ ] Tests and proper CI/CD pipeline.
- [ ] RNS hot reload
- [ ] Backup/Import identities, messages and interfaces.
- [ ] Full LXST support.
- [ ] Move to Poetry and pyproject.toml for Python packaging.
- [x] Poetry for packaging and dependency management.
- [x] More stats on about page.
- [x] Actions are pinned to full-length SHA hashes.
- [x] Docker images are smaller and use SHA256 hashes for the images.
- [x] Electron improvements.
- [x] Electron improvements (ASAR and security).
- [x] Latest updates for NPM and Python dependencies (bleeding edge)
- [x] Numerous Ruff, Deepsource, CodeQL Advanced and Bearer Linting/SAST fixes.
- [x] Some performance improvements.
@@ -35,25 +35,76 @@ Check [releases](https://github.com/Sudo-Ivan/reticulum-meshchatX/releases) for
## Building
coderabbitai[bot] commented 2025-12-01 06:13:58 +00:00 (Migrated from github.com)
Review

⚠️ Potential issue | 🟡 Minor

Documentation references outdated setup.py.

The text mentions setup.py but the PR appears to have migrated to Poetry with pyproject.toml. The cx_setup.py is for cx_Freeze, not for pip packaging.

Consider updating to reflect the Poetry workflow:

-The backend now provides `pyproject.toml` so you can build/install a wheel with `pip install .` or `python -m build`. Before packaging, run `python3 scripts/sync_version.py` (or `make sync-version`) so the generated `src/version.py` reflects the `package.json` version that the Electron artifacts use. The same version helper drives `meshchat.get_app_version()` and `setup.py`, so the CLI release metadata, wheel and AppImage/NSIS bundles stay aligned.
+The backend now provides `pyproject.toml` so you can build/install a wheel with `pip install .` or `poetry build`. Before packaging, run `python3 scripts/sync_version.py` (or `make sync-version`) so the generated `src/version.py` reflects the `package.json` version that the Electron artifacts use. The same version helper drives `meshchat.get_app_version()` and `cx_setup.py`, so the CLI release metadata, wheel and AppImage/NSIS bundles stay aligned.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

## Python packaging

The backend now provides `pyproject.toml` so you can build/install a wheel with `pip install .` or `poetry build`. Before packaging, run `python3 scripts/sync_version.py` (or `make sync-version`) so the generated `src/version.py` reflects the `package.json` version that the Electron artifacts use. The same version helper drives `meshchat.get_app_version()` and `cx_setup.py`, so the CLI release metadata, wheel and AppImage/NSIS bundles stay aligned.
🤖 Prompt for AI Agents
In README.md around lines 63 to 65, the documentation still references setup.py
and causes confusion after migrating packaging to pyproject.toml/Poetry; update
the Python packaging section to remove references to setup.py and cx_setup.py,
explain the Poetry/pyproject.toml workflow (how to build/install with pip or
python -m build if still supported), and instruct to run the existing version
sync script (python3 scripts/sync_version.py or make sync-version) so
src/version.py matches package.json; also note that cx_setup.py is for cx_Freeze
(used for AppImage/NSIS) and should be documented separately if relevant.

Addressed in commits ac839df to c98131f

_⚠️ Potential issue_ | _🟡 Minor_ **Documentation references outdated `setup.py`.** The text mentions `setup.py` but the PR appears to have migrated to Poetry with `pyproject.toml`. The `cx_setup.py` is for cx_Freeze, not for pip packaging. Consider updating to reflect the Poetry workflow: ```diff -The backend now provides `pyproject.toml` so you can build/install a wheel with `pip install .` or `python -m build`. Before packaging, run `python3 scripts/sync_version.py` (or `make sync-version`) so the generated `src/version.py` reflects the `package.json` version that the Electron artifacts use. The same version helper drives `meshchat.get_app_version()` and `setup.py`, so the CLI release metadata, wheel and AppImage/NSIS bundles stay aligned. +The backend now provides `pyproject.toml` so you can build/install a wheel with `pip install .` or `poetry build`. Before packaging, run `python3 scripts/sync_version.py` (or `make sync-version`) so the generated `src/version.py` reflects the `package.json` version that the Electron artifacts use. The same version helper drives `meshchat.get_app_version()` and `cx_setup.py`, so the CLI release metadata, wheel and AppImage/NSIS bundles stay aligned. ``` <!-- suggestion_start --> <details> <summary>📝 Committable suggestion</summary> > ‼️ **IMPORTANT** > Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements. ```suggestion ## Python packaging The backend now provides `pyproject.toml` so you can build/install a wheel with `pip install .` or `poetry build`. Before packaging, run `python3 scripts/sync_version.py` (or `make sync-version`) so the generated `src/version.py` reflects the `package.json` version that the Electron artifacts use. The same version helper drives `meshchat.get_app_version()` and `cx_setup.py`, so the CLI release metadata, wheel and AppImage/NSIS bundles stay aligned. ``` </details> <!-- suggestion_end --> <details> <summary>🤖 Prompt for AI Agents</summary> ``` In README.md around lines 63 to 65, the documentation still references setup.py and causes confusion after migrating packaging to pyproject.toml/Poetry; update the Python packaging section to remove references to setup.py and cx_setup.py, explain the Poetry/pyproject.toml workflow (how to build/install with pip or python -m build if still supported), and instruct to run the existing version sync script (python3 scripts/sync_version.py or make sync-version) so src/version.py matches package.json; also note that cx_setup.py is for cx_Freeze (used for AppImage/NSIS) and should be documented separately if relevant. ``` </details> <!-- fingerprinting:phantom:medusa:ocelot --> <!-- This is an auto-generated comment by CodeRabbit --> ✅ Addressed in commits ac839df to c98131f
```bash
make install
make install # installs Python deps via Poetry and Node deps via npm
make build
```
You can run `make run` or `make develop` (a thin alias) to start the backend + frontend loop locally through `poetry run meshchat`.
### Python packaging
The Python build is driven entirely by Poetry now. Run `python3 scripts/sync_version.py` or `make sync-version` before packaging so `pyproject.toml` and `src/version.py` match `package.json`. After that:
```bash
python -m poetry install
make wheel # produces a wheel in python-dist/ that bundles the public assets
```
The wheel includes the frontend `public/` assets, `logo/`, and the CLI entry point, and `python-dist/` keeps the artifact separate from the Electron `dist/` output.
### Building in Docker
```bash
make docker-build
make build-docker
```
The build will be in the `dist` directory.
`build-docker` creates `reticulum-meshchatx:local` (or `$(DOCKER_IMAGE)` if you override it) via `docker buildx`. Set `DOCKER_PLATFORMS` to `linux/amd64,linux/arm64` when you need multi-arch images, and adjust `DOCKER_BUILD_FLAGS`/`DOCKER_BUILD_ARGS` to control `--load`/`--push`.
## Development
### Running with Docker Compose
```bash
make develop
make run-docker
```
`run-docker` feeds the locally-built image into `docker compose -f docker-compose.yml up --remove-orphans --pull never reticulum-meshchatx`. The compose file uses the `MESHCHAT_IMAGE` env var so you can override the target image without editing the YAML (the default still points at `ghcr.io/sudo-ivan/reticulum-meshchatx:latest`). Use `docker compose down` or `Ctrl+C` to stop the container.
The Electron build artifacts will still live under `dist/` for releases.
## Python packaging
The backend now provides `pyproject.toml` so you can build/install a wheel with `pip install .` or `python -m build`. Before packaging, run `python3 scripts/sync_version.py` (or `make sync-version`) so the generated `src/version.py` reflects the `package.json` version that the Electron artifacts use. The same version helper drives `meshchat.get_app_version()` and `setup.py`, so the CLI release metadata, wheel and AppImage/NSIS bundles stay aligned.
The backend uses Poetry with `pyproject.toml` for dependency management and packaging. Before building, run `python3 scripts/sync_version.py` (or `make sync-version`) to ensure the generated `src/version.py` reflects the version from `package.json` that the Electron artifacts use. This keeps the CLI release metadata, wheel packages, and other bundles aligned.
### Build artifact locations
Both `poetry build` and `python -m build` generate wheels inside the default `dist/` directory. The `make wheel` shortcut wraps `poetry build -f wheel` and then runs `python scripts/move_wheels.py` to relocate the generated `.whl` files into `python-dist/` (the layout expected by `scripts/test_wheel.sh` and the release automation). Use `make wheel` if you need the artifacts in `python-dist/`; `poetry build` or `python -m build` alone will leave them in `dist/`.
coderabbitai[bot] commented 2025-12-01 17:53:28 +00:00 (Migrated from github.com)
Review

🛠️ Refactor suggestion | 🟠 Major

Inconsistent Python invocation: use python3 consistently.

Line 46 uses python while line 75 uses python3 for the same sync_version.py script. Since Python 3 is standard and line 75 explicitly specifies python3, line 46 should do the same for consistency.

Apply this diff:

 The Python build is driven entirely by Poetry now. Run `python scripts/sync_version.py` or `make sync-version` before packaging so `pyproject.toml` and `src/version.py` match `package.json`. After that:

-python -m poetry install
+python3 scripts/sync_version.py
-make wheel  # produces a wheel in python-dist/ that bundles the public assets
+make wheel

Or more simply, update line 46:

-Run `python scripts/sync_version.py` or `make sync-version` before packaging so `pyproject.toml` and `src/version.py` match `package.json`. After that:
+Run `python3 scripts/sync_version.py` or `make sync-version` before packaging so `pyproject.toml` and `src/version.py` match `package.json`. After that:
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

### Python packaging

The Python build is driven entirely by Poetry now. Run `python3 scripts/sync_version.py` or `make sync-version` before packaging so `pyproject.toml` and `src/version.py` match `package.json`. After that:

🤖 Prompt for AI Agents
In README.md around lines 44 to 51, the invocation for the sync_version script
uses `python` while elsewhere (line 75) `python3` is used; update the command on
line 46 to use `python3` (i.e., replace `python scripts/sync_version.py` with
`python3 scripts/sync_version.py`) so the README consistently references Python
3 for running the script.

Addressed in commits 284517b to 3848613

_🛠️ Refactor suggestion_ | _🟠 Major_ **Inconsistent Python invocation: use `python3` consistently.** Line 46 uses `python` while line 75 uses `python3` for the same `sync_version.py` script. Since Python 3 is standard and line 75 explicitly specifies `python3`, line 46 should do the same for consistency. Apply this diff: ```diff The Python build is driven entirely by Poetry now. Run `python scripts/sync_version.py` or `make sync-version` before packaging so `pyproject.toml` and `src/version.py` match `package.json`. After that: -python -m poetry install +python3 scripts/sync_version.py -make wheel # produces a wheel in python-dist/ that bundles the public assets +make wheel ``` Or more simply, update line 46: ```diff -Run `python scripts/sync_version.py` or `make sync-version` before packaging so `pyproject.toml` and `src/version.py` match `package.json`. After that: +Run `python3 scripts/sync_version.py` or `make sync-version` before packaging so `pyproject.toml` and `src/version.py` match `package.json`. After that: ``` <!-- suggestion_start --> <details> <summary>📝 Committable suggestion</summary> > ‼️ **IMPORTANT** > Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements. ```suggestion ### Python packaging The Python build is driven entirely by Poetry now. Run `python3 scripts/sync_version.py` or `make sync-version` before packaging so `pyproject.toml` and `src/version.py` match `package.json`. After that: ``` </details> <!-- suggestion_end --> <details> <summary>🤖 Prompt for AI Agents</summary> ``` In README.md around lines 44 to 51, the invocation for the sync_version script uses `python` while elsewhere (line 75) `python3` is used; update the command on line 46 to use `python3` (i.e., replace `python scripts/sync_version.py` with `python3 scripts/sync_version.py`) so the README consistently references Python 3 for running the script. ``` </details> <!-- This is an auto-generated comment by CodeRabbit --> ✅ Addressed in commits 284517b to 3848613
### Building with Poetry
```bash
# Install dependencies
poetry install
# Build the package (wheels land in dist/)
poetry build
# Install locally for testing (consumes dist/)
pip install dist/*.whl
```
### Building with pip (alternative)
If you prefer pip, you can build/install directly:
```bash
# Build the wheel
pip install build
python -m build
# Install locally
pip install .
```
### cx_Freeze (for AppImage/NSIS)
The `cx_setup.py` script uses cx_Freeze for creating standalone executables (AppImage for Linux, NSIS for Windows). This is separate from the Poetry/pip packaging workflow.

47
cx_setup.py Normal file
View File

@@ -0,0 +1,47 @@
from pathlib import Path
from cx_Freeze import Executable, setup
from meshchatx.src.version import __version__
ROOT = Path(__file__).resolve().parent
PUBLIC_DIR = ROOT / "meshchatx" / "public"
include_files = [
(str(PUBLIC_DIR), "public"),
("logo", "logo"),
]
setup(
name="ReticulumMeshChatX",
version=__version__,
description="A simple mesh network communications app powered by the Reticulum Network Stack",
executables=[
Executable(
script="meshchatx/meshchat.py",
base=None,
target_name="ReticulumMeshChatX",
shortcut_name="ReticulumMeshChatX",
shortcut_dir="ProgramMenuFolder",
icon="logo/icon.ico",
),
],
options={
"build_exe": {
"packages": [
"RNS",
"RNS.Interfaces",
"LXMF",
],
"include_files": include_files,
"excludes": [
"PIL",
],
"optimize": 2,
"build_exe": "build/exe",
"replace_paths": [
("*", ""),
],
},
},
)

View File

@@ -1,7 +1,7 @@
services:
reticulum-meshchatx:
container_name: reticulum-meshchatx
image: ghcr.io/sudo-ivan/reticulum-meshchatx:latest
image: ${MESHCHAT_IMAGE:-ghcr.io/sudo-ivan/reticulum-meshchatx:latest}
pull_policy: always
restart: unless-stopped
# Make the meshchat web interface accessible from the host on port 8000

View File

@@ -133,6 +133,14 @@ app.whenReady().then(async () => {
webPreferences: {
// used to inject logging over ipc
preload: path.join(__dirname, 'preload.js'),
// Security: disable node integration in renderer
nodeIntegration: false,
// Security: enable context isolation (default in Electron 12+)
contextIsolation: true,
// Security: enable sandbox for additional protection
sandbox: true,
// Security: disable remote module (deprecated but explicit)
enableRemoteModule: false,
},
});

3
meshchatx/__init__.py Normal file
View File

@@ -0,0 +1,3 @@
"""Reticulum MeshChatX - A mesh network communications app."""
__version__ = "2.41.0"

View File

@@ -1,4 +1,4 @@
from datetime import datetime, timezone
from datetime import UTC, datetime
from peewee import * # noqa: F403
from playhouse.migrate import SqliteMigrator
@@ -68,8 +68,8 @@ class Config(BaseModel):
id = BigAutoField() # noqa: F405
key = CharField(unique=True) # noqa: F405
value = TextField() # noqa: F405
created_at = DateTimeField(default=lambda: datetime.now(timezone.utc)) # noqa: F405
updated_at = DateTimeField(default=lambda: datetime.now(timezone.utc)) # noqa: F405
created_at = DateTimeField(default=lambda: datetime.now(UTC)) # noqa: F405
updated_at = DateTimeField(default=lambda: datetime.now(UTC)) # noqa: F405
# define table name
class Meta:
@@ -95,8 +95,8 @@ class Announce(BaseModel):
snr = FloatField(null=True) # noqa: F405
quality = FloatField(null=True) # noqa: F405
created_at = DateTimeField(default=lambda: datetime.now(timezone.utc)) # noqa: F405
updated_at = DateTimeField(default=lambda: datetime.now(timezone.utc)) # noqa: F405
created_at = DateTimeField(default=lambda: datetime.now(UTC)) # noqa: F405
updated_at = DateTimeField(default=lambda: datetime.now(UTC)) # noqa: F405
# define table name
class Meta:
@@ -108,8 +108,8 @@ class CustomDestinationDisplayName(BaseModel):
destination_hash = CharField(unique=True) # noqa: F405 # unique destination hash
display_name = CharField() # noqa: F405 # custom display name for the destination hash
created_at = DateTimeField(default=lambda: datetime.now(timezone.utc)) # noqa: F405
updated_at = DateTimeField(default=lambda: datetime.now(timezone.utc)) # noqa: F405
created_at = DateTimeField(default=lambda: datetime.now(UTC)) # noqa: F405
updated_at = DateTimeField(default=lambda: datetime.now(UTC)) # noqa: F405
# define table name
class Meta:
@@ -122,8 +122,8 @@ class FavouriteDestination(BaseModel):
display_name = CharField() # noqa: F405 # custom display name for the destination hash
aspect = CharField() # noqa: F405 # e.g: nomadnetwork.node
created_at = DateTimeField(default=lambda: datetime.now(timezone.utc)) # noqa: F405
updated_at = DateTimeField(default=lambda: datetime.now(timezone.utc)) # noqa: F405
created_at = DateTimeField(default=lambda: datetime.now(UTC)) # noqa: F405
updated_at = DateTimeField(default=lambda: datetime.now(UTC)) # noqa: F405
# define table name
class Meta:
@@ -159,8 +159,8 @@ class LxmfMessage(BaseModel):
snr = FloatField(null=True) # noqa: F405
quality = FloatField(null=True) # noqa: F405
is_spam = BooleanField(default=False) # noqa: F405 # if true, message is marked as spam
created_at = DateTimeField(default=lambda: datetime.now(timezone.utc)) # noqa: F405
updated_at = DateTimeField(default=lambda: datetime.now(timezone.utc)) # noqa: F405
created_at = DateTimeField(default=lambda: datetime.now(UTC)) # noqa: F405
updated_at = DateTimeField(default=lambda: datetime.now(UTC)) # noqa: F405
# define table name
class Meta:
@@ -172,8 +172,8 @@ class LxmfConversationReadState(BaseModel):
destination_hash = CharField(unique=True) # noqa: F405 # unique destination hash
last_read_at = DateTimeField() # noqa: F405
created_at = DateTimeField(default=lambda: datetime.now(timezone.utc)) # noqa: F405
updated_at = DateTimeField(default=lambda: datetime.now(timezone.utc)) # noqa: F405
created_at = DateTimeField(default=lambda: datetime.now(UTC)) # noqa: F405
updated_at = DateTimeField(default=lambda: datetime.now(UTC)) # noqa: F405
# define table name
class Meta:
@@ -189,8 +189,8 @@ class LxmfUserIcon(BaseModel):
CharField() # noqa: F405
) # hex colour to use for background (background colour)
created_at = DateTimeField(default=lambda: datetime.now(timezone.utc)) # noqa: F405
updated_at = DateTimeField(default=lambda: datetime.now(timezone.utc)) # noqa: F405
created_at = DateTimeField(default=lambda: datetime.now(UTC)) # noqa: F405
updated_at = DateTimeField(default=lambda: datetime.now(UTC)) # noqa: F405
# define table name
class Meta:
@@ -203,8 +203,8 @@ class BlockedDestination(BaseModel):
unique=True,
index=True,
) # unique destination hash that is blocked
created_at = DateTimeField(default=lambda: datetime.now(timezone.utc)) # noqa: F405
updated_at = DateTimeField(default=lambda: datetime.now(timezone.utc)) # noqa: F405
created_at = DateTimeField(default=lambda: datetime.now(UTC)) # noqa: F405
updated_at = DateTimeField(default=lambda: datetime.now(UTC)) # noqa: F405
# define table name
class Meta:
@@ -217,8 +217,8 @@ class SpamKeyword(BaseModel):
unique=True,
index=True,
) # keyword to match against message content
created_at = DateTimeField(default=lambda: datetime.now(timezone.utc)) # noqa: F405
updated_at = DateTimeField(default=lambda: datetime.now(timezone.utc)) # noqa: F405
created_at = DateTimeField(default=lambda: datetime.now(UTC)) # noqa: F405
updated_at = DateTimeField(default=lambda: datetime.now(UTC)) # noqa: F405
# define table name
class Meta:

View File

@@ -13,7 +13,7 @@ import threading
import time
import webbrowser
from collections.abc import Callable
from datetime import datetime, timezone
from datetime import UTC, datetime
import LXMF
import psutil
@@ -24,32 +24,46 @@ from LXMF import LXMRouter
from peewee import SqliteDatabase
from serial.tools import list_ports
import database
from src.backend.announce_handler import AnnounceHandler
from src.backend.async_utils import AsyncUtils
from src.backend.audio_call_manager import AudioCall, AudioCallManager
from src.backend.colour_utils import ColourUtils
from src.backend.interface_config_parser import InterfaceConfigParser
from src.backend.interface_editor import InterfaceEditor
from src.backend.lxmf_message_fields import (
from meshchatx import database
from meshchatx.src.backend.announce_handler import AnnounceHandler
from meshchatx.src.backend.async_utils import AsyncUtils
from meshchatx.src.backend.audio_call_manager import AudioCall, AudioCallManager
from meshchatx.src.backend.colour_utils import ColourUtils
from meshchatx.src.backend.interface_config_parser import InterfaceConfigParser
from meshchatx.src.backend.interface_editor import InterfaceEditor
from meshchatx.src.backend.lxmf_message_fields import (
LxmfAudioField,
LxmfFileAttachment,
LxmfFileAttachmentsField,
LxmfImageField,
)
from src.backend.sideband_commands import SidebandCommands
from meshchatx.src.backend.sideband_commands import SidebandCommands
from meshchatx.src.version import __version__ as app_version
# NOTE: this is required to be able to pack our app with cxfreeze as an exe, otherwise it can't access bundled assets
# this returns a file path based on if we are running meshchat.py directly, or if we have packed it as an exe with cxfreeze
# https://cx-freeze.readthedocs.io/en/latest/faq.html#using-data-files
# bearer:disable python_lang_path_traversal
def get_file_path(filename):
if getattr(sys, "frozen", False):
datadir = os.path.dirname(sys.executable)
else:
datadir = os.path.dirname(__file__)
return os.path.join(datadir, filename)
# Assets live inside the meshchatx package when installed from a wheel
package_dir = os.path.dirname(__file__)
package_path = os.path.join(package_dir, filename)
if os.path.exists(package_path):
return package_path
# When running from the repository, fall back to the project root
repo_root = os.path.dirname(package_dir)
repo_path = os.path.join(repo_root, filename)
if os.path.exists(repo_path):
return repo_path
return package_path
class ReticulumMeshChat:
def __init__(self, identity: RNS.Identity, storage_dir, reticulum_config_dir):
@@ -225,12 +239,10 @@ class ReticulumMeshChat:
thread.daemon = True
thread.start()
# gets app version from package.json
# gets app version from the synchronized Python version helper
@staticmethod
def get_app_version() -> str:
with open(get_file_path("package.json")) as f:
package_json = json.load(f)
return package_json["version"]
return app_version
# automatically announces based on user config
async def announce_loop(self):
@@ -1104,10 +1116,10 @@ class ReticulumMeshChat:
interfaces[interface_name] = interface_details
# handle SerialInterface, KISSInterface, and AX25KISSInterface
if (
interface_type == "SerialInterface"
or interface_type == "KISSInterface"
or interface_type == "AX25KISSInterface"
if interface_type in (
"SerialInterface",
"KISSInterface",
"AX25KISSInterface",
):
# ensure port provided
interface_port = data.get("port")
@@ -1129,10 +1141,7 @@ class ReticulumMeshChat:
InterfaceEditor.update_value(interface_details, data, "stopbits")
# Handle KISS and AX25KISS specific options
if (
interface_type == "KISSInterface"
or interface_type == "AX25KISSInterface"
):
if interface_type in ("KISSInterface", "AX25KISSInterface"):
# set optional options
InterfaceEditor.update_value(interface_details, data, "preamble")
InterfaceEditor.update_value(interface_details, data, "txtail")
@@ -1379,7 +1388,7 @@ class ReticulumMeshChat:
print(e)
elif msg.type == WSMsgType.ERROR:
# ignore errors while handling message
print("ws connection error %s" % websocket_response.exception())
print(f"ws connection error {websocket_response.exception()}")
# websocket closed
self.websocket_clients.remove(websocket_response)
@@ -1702,7 +1711,7 @@ class ReticulumMeshChat:
print(e)
elif msg.type == WSMsgType.ERROR:
# ignore errors while handling message
print("ws connection error %s" % websocket_response.exception())
print(f"ws connection error {websocket_response.exception()}")
# unregister audio packet handler now that the websocket has been closed
audio_call.register_audio_packet_listener(on_audio_packet)
@@ -2052,9 +2061,7 @@ class ReticulumMeshChat:
# check if user wants to request the path from the network right now
request_query_param = request.query.get("request", "false")
should_request_now = (
request_query_param == "true" or request_query_param == "1"
)
should_request_now = request_query_param in ("true", "1")
if should_request_now:
# determine how long we should wait for a path response
timeout_seconds = int(request.query.get("timeout", 15))
@@ -3011,13 +3018,38 @@ class ReticulumMeshChat:
)
if message:
message.is_spam = is_spam
message.updated_at = datetime.now(timezone.utc)
message.updated_at = datetime.now(UTC)
message.save()
return web.json_response({"message": "ok"})
return web.json_response({"error": "Message not found"}, status=404)
except Exception as e:
return web.json_response({"error": str(e)}, status=500)
# security headers middleware
@web.middleware
async def security_middleware(request, handler):
response = await handler(request)
# Add security headers to all responses
response.headers["X-Content-Type-Options"] = "nosniff"
response.headers["X-Frame-Options"] = "DENY"
response.headers["X-XSS-Protection"] = "1; mode=block"
response.headers["Referrer-Policy"] = "strict-origin-when-cross-origin"
# CSP: allow localhost for development and Electron, websockets, and blob URLs
csp = (
"default-src 'self'; "
"script-src 'self' 'unsafe-inline' 'unsafe-eval'; "
"style-src 'self' 'unsafe-inline'; "
"img-src 'self' data: blob:; "
"font-src 'self' data:; "
"connect-src 'self' ws://localhost:* wss://localhost:* blob:; "
"media-src 'self' blob:; "
"worker-src 'self' blob:; "
"object-src 'none'; "
"base-uri 'self';"
)
response.headers["Content-Security-Policy"] = csp
return response
# called when web app has started
async def on_startup(app):
# remember main event loop
@@ -3033,6 +3065,7 @@ class ReticulumMeshChat:
# create and run web app
app = web.Application(
client_max_size=1024 * 1024 * 50,
middlewares=[security_middleware],
) # allow uploading files up to 50mb
app.add_routes(routes)
app.add_routes(
@@ -3886,7 +3919,7 @@ class ReticulumMeshChat:
"icon_name": icon_name,
"foreground_colour": foreground_colour,
"background_colour": background_colour,
"updated_at": datetime.now(timezone.utc),
"updated_at": datetime.now(UTC),
}
# upsert to database
@@ -4108,7 +4141,7 @@ class ReticulumMeshChat:
"snr": lxmf_message_dict["snr"],
"quality": lxmf_message_dict["quality"],
"is_spam": is_spam,
"updated_at": datetime.now(timezone.utc),
"updated_at": datetime.now(UTC),
}
# upsert to database
@@ -4144,7 +4177,7 @@ class ReticulumMeshChat:
"rssi": rssi,
"snr": snr,
"quality": quality,
"updated_at": datetime.now(timezone.utc),
"updated_at": datetime.now(UTC),
}
# only set app data if provided, as we don't want to wipe existing data when we request keys from the network
@@ -4170,7 +4203,7 @@ class ReticulumMeshChat:
data = {
"destination_hash": destination_hash,
"display_name": display_name,
"updated_at": datetime.now(timezone.utc),
"updated_at": datetime.now(UTC),
}
# upsert to database
@@ -4193,7 +4226,7 @@ class ReticulumMeshChat:
"destination_hash": destination_hash,
"display_name": display_name,
"aspect": aspect,
"updated_at": datetime.now(timezone.utc),
"updated_at": datetime.now(UTC),
}
# upsert to database
@@ -4210,8 +4243,8 @@ class ReticulumMeshChat:
# prepare data to insert or update
data = {
"destination_hash": destination_hash,
"last_read_at": datetime.now(timezone.utc),
"updated_at": datetime.now(timezone.utc),
"last_read_at": datetime.now(UTC),
"updated_at": datetime.now(UTC),
}
# upsert to database
@@ -4878,7 +4911,7 @@ class Config:
data = {
"key": key,
"value": value,
"updated_at": datetime.now(timezone.utc),
"updated_at": datetime.now(UTC),
}
# upsert to database
@@ -5022,8 +5055,8 @@ class NomadnetDownloader:
self.path = path
self.data = data
self.timeout = timeout
self.on_download_success = on_download_success
self.on_download_failure = on_download_failure
self._download_success_callback = on_download_success
self._download_failure_callback = on_download_failure
self.on_progress_update = on_progress_update
self.request_receipt = None
self.is_cancelled = False
@@ -5048,7 +5081,7 @@ class NomadnetDownloader:
pass
# notify that download was cancelled
self.on_download_failure("cancelled")
self._download_failure_callback("cancelled")
# setup link to destination and request download
async def download(
@@ -5088,7 +5121,7 @@ class NomadnetDownloader:
# if we still don't have a path, we can't establish a link, so bail out
if not RNS.Transport.has_path(self.destination_hash):
self.on_download_failure("Could not find path to destination.")
self._download_failure_callback("Could not find path to destination.")
return
# check if cancelled before establishing link
@@ -5124,7 +5157,7 @@ class NomadnetDownloader:
# if we still haven't established a link, bail out
if link.status is not RNS.Link.ACTIVE:
self.on_download_failure("Could not establish link to destination.")
self._download_failure_callback("Could not establish link to destination.")
# link to destination was established, we should now request the download
def link_established(self, link):
@@ -5147,11 +5180,11 @@ class NomadnetDownloader:
# handle successful download
def on_response(self, request_receipt: RNS.RequestReceipt):
self.on_download_success(request_receipt)
self._download_success_callback(request_receipt)
# handle failure
def on_failed(self, request_receipt=None):
self.on_download_failure("request_failed")
self._download_failure_callback("request_failed")
# handle download progress
def on_progress(self, request_receipt):

View File

@@ -0,0 +1 @@
"""Backend utilities shared by the Reticulum MeshChatX CLI."""

View File

@@ -3,9 +3,8 @@ import time
import RNS
from RNS.Interfaces.Interface import Interface
from websockets.sync.server import Server, ServerConnection, serve
from src.backend.interfaces.WebsocketClientInterface import WebsocketClientInterface
from websockets.sync.server import Server, ServerConnection, serve
class WebsocketServerInterface(Interface):

View File

@@ -0,0 +1 @@
"""Shared transport interfaces for MeshChatX."""

View File

Before

Width:  |  Height:  |  Size: 109 KiB

After

Width:  |  Height:  |  Size: 109 KiB

View File

Before

Width:  |  Height:  |  Size: 80 KiB

After

Width:  |  Height:  |  Size: 80 KiB

View File

Before

Width:  |  Height:  |  Size: 8.2 KiB

After

Width:  |  Height:  |  Size: 8.2 KiB

View File

Before

Width:  |  Height:  |  Size: 8.1 KiB

After

Width:  |  Height:  |  Size: 8.1 KiB

View File

Before

Width:  |  Height:  |  Size: 8.0 KiB

After

Width:  |  Height:  |  Size: 8.0 KiB

View File

Before

Width:  |  Height:  |  Size: 8.1 KiB

After

Width:  |  Height:  |  Size: 8.1 KiB

View File

Before

Width:  |  Height:  |  Size: 85 KiB

After

Width:  |  Height:  |  Size: 85 KiB

View File

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Some files were not shown because too many files have changed in this diff Show More