64 Commits
Stats ... main

Author SHA1 Message Date
3438b271a5 Update dependencies in poetry.lock and pyproject.toml
Some checks failed
Build and Publish Docker Image / build (push) Failing after 3s
Run Tests / test (ubuntu-latest, 3.9) (push) Failing after 3s
Docker Build Test / build (3.10) (push) Successful in 56s
Run Tests / test (windows-latest, 3.13) (push) Has been cancelled
Run Tests / test (windows-latest, 3.9) (push) Has been cancelled
Docker Build Test / build (3.13) (push) Failing after 4s
Docker Build Test / build (3.9) (push) Failing after 4s
Safety / security (push) Failing after 3s
Run Tests / test (ubuntu-latest, 3.10) (push) Failing after 2s
Run Tests / test (ubuntu-latest, 3.11) (push) Failing after 2s
Run Tests / test (ubuntu-latest, 3.12) (push) Failing after 2s
Run Tests / test (ubuntu-latest, 3.13) (push) Failing after 3s
Docker Build Test / build (3.11) (push) Successful in 54s
Docker Build Test / build (3.12) (push) Successful in 52s
Run Tests / test (windows-latest, 3.10) (push) Has been cancelled
Run Tests / test (windows-latest, 3.11) (push) Has been cancelled
Run Tests / test (windows-latest, 3.12) (push) Has been cancelled
- Removed old version of `cryptography` (43.0.3) and updated to version 46.0.3 with adjusted markers.
- Added `typing-extensions` package (4.15.0) to support type hints for Python 3.9+.
- Updated Python version constraints in `pyproject.toml` for better compatibility.
2025-12-02 11:27:36 -06:00
d6228d6d63 Merge pull request 'refactor-pagenode-logic' (#1) from refactor-pagenode-logic into main
Some checks failed
Docker Build Test / build (3.10) (push) Failing after 3s
Run Tests / test (ubuntu-latest, 3.13) (push) Failing after 2s
Docker Build Test / build (3.11) (push) Failing after 2s
Docker Build Test / build (3.12) (push) Failing after 2s
Docker Build Test / build (3.13) (push) Failing after 2s
Docker Build Test / build (3.9) (push) Failing after 2s
Build and Publish Docker Image / build (push) Failing after 3s
Safety / security (push) Failing after 2s
Run Tests / test (ubuntu-latest, 3.10) (push) Failing after 2s
Run Tests / test (ubuntu-latest, 3.11) (push) Failing after 2s
Run Tests / test (ubuntu-latest, 3.12) (push) Failing after 1s
Run Tests / test (ubuntu-latest, 3.9) (push) Failing after 1s
Run Tests / test (windows-latest, 3.10) (push) Has been cancelled
Run Tests / test (windows-latest, 3.11) (push) Has been cancelled
Run Tests / test (windows-latest, 3.12) (push) Has been cancelled
Run Tests / test (windows-latest, 3.13) (push) Has been cancelled
Run Tests / test (windows-latest, 3.9) (push) Has been cancelled
Reviewed-on: Ivan/rns-page-node#1
2025-12-02 17:26:05 +00:00
ccf954681b Refactor path handling in PageNode class for improved reliability
Some checks failed
Docker Build Test / build (3.11) (pull_request) Successful in 1m9s
Docker Build Test / build (3.10) (pull_request) Successful in 1m12s
Docker Build Test / build (3.12) (pull_request) Successful in 1m8s
Docker Build Test / build (3.13) (pull_request) Failing after 2s
Build and Publish Docker Image / build (pull_request) Failing after 2s
Run Tests / test (windows-latest, 3.10) (pull_request) Has been cancelled
Run Tests / test (windows-latest, 3.11) (pull_request) Has been cancelled
Run Tests / test (windows-latest, 3.12) (pull_request) Has been cancelled
Run Tests / test (windows-latest, 3.13) (pull_request) Has been cancelled
Run Tests / test (windows-latest, 3.9) (pull_request) Has been cancelled
Docker Build Test / build (3.9) (pull_request) Failing after 2s
Run Tests / test (ubuntu-latest, 3.10) (pull_request) Failing after 2s
Run Tests / test (ubuntu-latest, 3.11) (pull_request) Failing after 2s
Run Tests / test (ubuntu-latest, 3.12) (pull_request) Failing after 1s
Run Tests / test (ubuntu-latest, 3.13) (pull_request) Failing after 1s
Run Tests / test (ubuntu-latest, 3.9) (pull_request) Failing after 2s
- Updated path resolution for pages and files to use `resolve()` method, ensuring absolute paths are handled correctly.
- Enhanced relative path calculation using `relative_to()` to improve robustness against invalid paths.
- Adjusted request path formatting to include a leading slash for consistency.
2025-12-02 11:03:58 -06:00
4ec44900cf add windows runner test 2025-12-02 11:02:01 -06:00
d4099fb9a2 Refactor _scan_pages method and enhance file reading logic in PageNode class
- Updated docstring for _scan_pages to clarify exclusion of .allowed files.
- Improved file reading logic to handle script detection and content retrieval more efficiently.
- Refined error handling during the announce process to catch specific exceptions.
2025-12-02 10:17:16 -06:00
1571b315b2 Add docstrings to PageNode methods for improved clarity 2025-12-02 10:06:56 -06:00
71bd49bd7d Refactor PageNode class to improve page and file registration logic
- Consolidated page and file scanning methods to return lists of served pages and files.
- Improved error handling in file reading operations.
- Updated the announce loop to use a more efficient waiting mechanism.
- Improved command-line argument handling for log level configuration.
2025-12-02 09:58:31 -06:00
382413dc08 Update to support immutable github releases/tags
Some checks failed
Docker Build Test / build (3.12) (push) Successful in 31s
Docker Build Test / build (3.13) (push) Successful in 37s
Docker Build Test / build (3.10) (push) Successful in 26s
Docker Build Test / build (3.11) (push) Successful in 25s
Docker Build Test / build (3.9) (push) Successful in 29s
Safety / security (push) Failing after 30s
Run Tests / test (3.10) (push) Successful in 40s
Run Tests / test (3.11) (push) Successful in 31s
Run Tests / test (3.12) (push) Successful in 35s
Build and Publish Docker Image / build (push) Failing after 1m15s
Run Tests / test (3.13) (push) Successful in 25s
Run Tests / test (3.9) (push) Successful in 23s
2025-11-23 11:45:41 -06:00
0621facc7d Add config example
Some checks failed
Docker Build Test / build (3.11) (push) Successful in 1m47s
Docker Build Test / build (3.13) (push) Successful in 1m44s
Docker Build Test / build (3.12) (push) Successful in 1m47s
Docker Build Test / build (3.10) (push) Successful in 1m51s
Docker Build Test / build (3.9) (push) Successful in 32s
Safety / security (push) Failing after 37s
Run Tests / test (3.10) (push) Successful in 1m9s
Run Tests / test (3.12) (push) Successful in 2m10s
Run Tests / test (3.11) (push) Successful in 2m18s
Run Tests / test (3.13) (push) Successful in 1m49s
Run Tests / test (3.9) (push) Successful in 49s
Publish Python 🐍 distribution 📦 to PyPI / Build distribution 📦 (push) Failing after 46s
Publish Python 🐍 distribution 📦 to PyPI / Publish Python 🐍 distribution 📦 to PyPI (push) Has been skipped
Publish Python 🐍 distribution 📦 to PyPI / Sign the Python 🐍 distribution 📦 and create GitHub Release (push) Has been skipped
Build and Publish Docker Image / build (push) Failing after 1m7s
2025-11-23 11:20:52 -06:00
50cbfed5fa Add configuration loading from file and update CLI argument handling
- Implemented `load_config` function to read key-value pairs from a configuration file.
- Enhanced `main` function to accept a configuration file path as a command-line argument.
- Introduced `get_config_value` function to prioritize values from CLI arguments, config file, or defaults.
- Updated logic to retrieve various configuration settings, including paths and intervals, from the loaded config.
2025-11-23 11:20:41 -06:00
36d9a3350b Update README.ru 2025-11-23 11:20:23 -06:00
515a9d9dbf Update README with command-line options and configuration file details 2025-11-23 11:20:11 -06:00
3c27b4f9b8 Update README files to reflect changes in announce interval 2025-11-23 11:08:36 -06:00
851c8c05d4 Update announce interval documentation and logic in PageNode class to reflect minutes instead of seconds 2025-11-23 11:08:09 -06:00
8002a75e26 Update README.ru 2025-11-23 11:03:41 -06:00
06e6b55ecc Update Makefile 2025-11-23 11:02:29 -06:00
48e47bd0bd Update README 2025-11-23 11:02:22 -06:00
9c074a0333 remove 2025-11-23 11:02:13 -06:00
f2314f862c Update cryptography package to version 46.0.3 in poetry.lock; update rns package to version 1.0.4 in pyproject.toml; add project classifiers and URLs. 2025-11-23 11:02:04 -06:00
6e57536650 Update project version to 1.3.0 and dependencies to rns 1.0.4 in pyproject.toml and requirements.txt 2025-11-23 10:58:55 -06:00
5fd7551874 Update and format test_client.py and test_client2.py for improved readability and structure; added second dictionary data handling in tests. 2025-11-23 10:58:47 -06:00
62d592c4d0 Fix environment variable handling in PageNode class to support forums and chats 2025-11-23 10:58:29 -06:00
8af2a9abbb Update README.ru.md
Some checks failed
Safety / security (push) Failing after 26s
Docker Build Test / build (3.11) (push) Successful in 24s
Docker Build Test / build (3.12) (push) Successful in 29s
Docker Build Test / build (3.13) (push) Successful in 28s
Run Tests / test (3.10) (push) Successful in 27s
Docker Build Test / build (3.10) (push) Successful in 49s
Docker Build Test / build (3.9) (push) Successful in 22s
Run Tests / test (3.11) (push) Successful in 28s
Run Tests / test (3.12) (push) Successful in 28s
Run Tests / test (3.13) (push) Successful in 26s
Build and Publish Docker Image / build (push) Failing after 1m11s
Run Tests / test (3.9) (push) Successful in 24s
2025-11-12 18:51:39 -06:00
64ca8bd4d2 add safety workflow 2025-11-12 18:47:23 -06:00
f1d025bd0e remove old 2025-11-12 18:47:08 -06:00
087ff563a2 update
Some checks failed
Docker Build Test / build (3.10) (push) Failing after 14s
Docker Build Test / build (3.11) (push) Successful in 53s
Docker Build Test / build (3.12) (push) Successful in 1m0s
Docker Build Test / build (3.13) (push) Successful in 58s
Docker Build Test / build (3.9) (push) Successful in 55s
Run Tests / test (3.12) (push) Successful in 51s
Build and Publish Docker Image / build (push) Failing after 1m10s
Run Tests / test (3.13) (push) Successful in 36s
Run Tests / test (3.9) (push) Successful in 32s
Run Tests / test (3.10) (push) Successful in 28s
Run Tests / test (3.11) (push) Successful in 30s
2025-11-12 18:40:01 -06:00
882dacf2bb Update dependencies to rns 1.0.2 in pyproject.toml and requirements.txt; refine README for clarity and usage instructions.
Some checks failed
Docker Build Test / build (3.12) (push) Failing after 1m16s
Docker Build Test / build (3.13) (push) Failing after 1m15s
Docker Build Test / build (3.11) (push) Failing after 1m19s
Docker Build Test / build (3.10) (push) Failing after 1m22s
Docker Build Test / build (3.9) (push) Failing after 26s
Run Tests / test (3.10) (push) Successful in 1m7s
Run Tests / test (3.11) (push) Successful in 1m5s
Run Tests / test (3.13) (push) Has been cancelled
Run Tests / test (3.12) (push) Has been cancelled
Build and Publish Docker Image / build (push) Has been cancelled
Run Tests / test (3.9) (push) Has been cancelled
2025-11-12 18:37:12 -06:00
a2efdb136a Update GitHub Actions workflow to specify Dockerfile path in the build context
Some checks failed
Docker Build Test / build (3.10) (push) Successful in 19s
Docker Build Test / build (3.13) (push) Successful in 27s
Docker Build Test / build (3.9) (push) Successful in 27s
Run Tests / test (3.10) (push) Successful in 37s
Run Tests / test (3.11) (push) Successful in 34s
Run Tests / test (3.12) (push) Successful in 55s
Run Tests / test (3.9) (push) Successful in 35s
Run Tests / test (3.13) (push) Successful in 45s
Publish Python 🐍 distribution 📦 to PyPI / Build distribution 📦 (push) Failing after 36s
Docker Build Test / build (3.11) (push) Successful in 23s
Docker Build Test / build (3.12) (push) Successful in 25s
Publish Python 🐍 distribution 📦 to PyPI / Publish Python 🐍 distribution 📦 to PyPI (push) Has been skipped
Publish Python 🐍 distribution 📦 to PyPI / Sign the Python 🐍 distribution 📦 and create GitHub Release (push) Has been skipped
Build and Publish Docker Image / build (push) Failing after 55s
2025-11-08 14:08:37 -06:00
001613b4fa add microvm
Some checks failed
Docker Build Test / build (3.10) (push) Successful in 3m4s
Docker Build Test / build (3.13) (push) Successful in 2m58s
Docker Build Test / build (3.11) (push) Successful in 3m3s
Docker Build Test / build (3.12) (push) Successful in 3m18s
Docker Build Test / build (3.9) (push) Successful in 1m32s
Build and Publish Docker Image / build (push) Has been cancelled
Run Tests / test (3.10) (push) Successful in 1m5s
Run Tests / test (3.11) (push) Successful in 1m19s
Run Tests / test (3.9) (push) Has been cancelled
Run Tests / test (3.12) (push) Has been cancelled
Run Tests / test (3.13) (push) Has been cancelled
2025-11-08 14:03:00 -06:00
74564d0ef2 Update Makefile to use docker buildx load command for building images and adjust Dockerfile paths to the new 'docker' directory. 2025-11-08 14:02:51 -06:00
81142ad194 Refactor GitHub Actions workflows to use Dockerfiles from the new 'docker' directory and add a new workflow for running tests across multiple Python versions. 2025-11-08 14:02:37 -06:00
fee1d2e2d6 Update package version to 1.2.0 and dependencies to rns 1.0.1 in pyproject.toml, requirements.txt, and setup.py; adjust poetry.lock accordingly. 2025-11-08 14:02:25 -06:00
7c93fdb71d move dockerfiles to docker folder 2025-11-08 14:02:06 -06:00
9e435eeebc Update test scripts to support environment variable processing and validate responses for different data types.
Some checks failed
Docker Build Test / build (3.11) (push) Successful in 9s
Docker Build Test / build (3.13) (push) Successful in 10s
Docker Build Test / build (3.12) (push) Successful in 39s
Docker Build Test / build (3.10) (push) Successful in 42s
Docker Build Test / build (3.9) (push) Successful in 38s
Build and Publish Docker Image / build (push) Failing after 9m35s
2025-10-05 16:41:01 -05:00
5dfcc1f2ce Improve data processing in PageNode class to handle both dictionary and bytes input.
Some checks failed
Docker Build Test / build (3.10) (push) Successful in 11s
Docker Build Test / build (3.12) (push) Successful in 11s
Docker Build Test / build (3.13) (push) Successful in 35s
Docker Build Test / build (3.11) (push) Successful in 37s
Docker Build Test / build (3.9) (push) Successful in 37s
Build and Publish Docker Image / build (push) Has been cancelled
2025-10-05 16:35:38 -05:00
2def60b457 Update GitHub Actions workflow to include Python 3.9 in the testing matrix
Some checks failed
Docker Build Test / build (3.11) (push) Successful in 28s
Docker Build Test / build (3.13) (push) Successful in 27s
Docker Build Test / build (3.10) (push) Successful in 3m19s
Docker Build Test / build (3.9) (push) Successful in 3m15s
Docker Build Test / build (3.12) (push) Successful in 3m26s
Build and Publish Docker Image / build (push) Failing after 9m33s
2025-10-05 16:10:18 -05:00
f708ad4ee1 Update python version requirement in setup.py to 3.9 2025-10-05 16:10:12 -05:00
f7568d81aa Adjust python version requirement to 3.9 2025-10-05 16:10:05 -05:00
251f9bacef Update .gitignore 2025-10-05 16:08:49 -05:00
07892dbfee Update README
Some checks failed
Docker Build Test / build (3.12) (push) Successful in 32s
Docker Build Test / build (3.10) (push) Successful in 36s
Docker Build Test / build (3.13) (push) Successful in 2m54s
Docker Build Test / build (3.11) (push) Successful in 2m57s
Build and Publish Docker Image / build (push) Has been cancelled
2025-10-05 16:04:33 -05:00
54e6849968 Improve path resolution in PageNode class to ensure security by validating file paths before serving. 2025-10-05 16:02:12 -05:00
ea27c380cb update version to 1.1.0 in setup.py 2025-10-05 15:49:10 -05:00
Sudo-Ivan
a338be85e1 update uv commands
Some checks failed
Docker Build Test / build (3.11) (push) Successful in 17s
Docker Build Test / build (3.12) (push) Successful in 16s
Docker Build Test / build (3.13) (push) Successful in 17s
Docker Build Test / build (3.10) (push) Successful in 40s
Build and Publish Docker Image / build (push) Failing after 9m34s
2025-10-01 03:03:53 -05:00
Sudo-Ivan
e31cb3418b Update
Some checks failed
Docker Build Test / build (3.11) (push) Successful in 34s
Docker Build Test / build (3.13) (push) Successful in 33s
Docker Build Test / build (3.12) (push) Has been cancelled
Docker Build Test / build (3.10) (push) Has been cancelled
Build and Publish Docker Image / build (push) Has been cancelled
2025-10-01 03:02:14 -05:00
Sudo-Ivan
798725dca6 Update
Some checks failed
Docker Build Test / build (3.12) (push) Successful in 51s
Docker Build Test / build (3.10) (push) Successful in 1m3s
Docker Build Test / build (3.11) (push) Successful in 1m38s
Docker Build Test / build (3.13) (push) Successful in 1m36s
Build and Publish Docker Image / build (push) Failing after 10m2s
2025-09-30 21:43:41 -05:00
Sudo-Ivan
6f393497f0 Add docstring 2025-09-30 21:37:51 -05:00
Sudo-Ivan
14b5aabf2b Improved PageNode class with documentation, error handling, and path management.
Update file and page serving methods to utilize pathlib for modern python path handling
2025-09-30 21:37:41 -05:00
fb36907447 Improve path handling in PageNode class to ensure consistent formatting of served pages and files. 2025-09-23 04:13:37 -05:00
62fde2617b Fix remote_identity assignment in PageNode class to use hash attribute 2025-09-23 03:00:06 -05:00
9f5ea23eb7 Improve request data parsing in PageNode class to support '|' delimiter and add handling for additional fields 2025-09-23 02:42:54 -05:00
19fad61706 Add Micron interactive features via environment variables 2025-09-23 02:28:55 -05:00
c900cf38c9 Bump to 1.1.0 2025-09-23 01:59:01 -05:00
014ebc25c6 Update default home page message and print node address in terminal output. 2025-09-23 01:57:19 -05:00
Sudo-Ivan
d5e9308fb5 Update GitHub Actions workflows to use updated action versions
- Updated actions/checkout to v5.0.0
- Updated actions/setup-python to v6.0.0
- Updated docker/build-push-action to v6.18.0
- Updated actions/upload-artifact to v4.6.2
- Updated actions/download-artifact to v5.0.0
- Updated sigstore/gh-action-sigstore-python to v3.0.1
2025-09-22 18:44:45 -05:00
Sudo-Ivan
7d5e891261 Update dependencies in poetry.lock and pyproject.toml
- Bump anyio from 4.9.0 to 4.10.0
- Bump authlib from 1.6.0 to 1.6.4
- Bump certifi from 2025.7.14 to 2025.8.3
- Bump cffi from 1.17.1 to 2.0.0
- Bump ruamel.yaml.clib from 0.2.12 to 0.2.13
- Bump ruff from 0.12.3 to 0.12.12
- Bump safety from 3.6.0 to 3.6.1
- Bump typer from 0.16.0 to 0.19.1
- Bump typing-extensions from 4.14.1 to 4.15.0
2025-09-22 14:24:57 -05:00
Sudo-Ivan
c382ed790f Update GitHub Actions workflows to use full-length commit hashes for actions 2025-09-22 14:24:40 -05:00
cb72e57da9 Merge pull request #2 from Sudo-Ivan/dependabot/github_actions/dot-github/workflows/pypa/gh-action-pypi-publish-1.13.0
Bump pypa/gh-action-pypi-publish from 1.12.3 to 1.13.0 in /.github/workflows
2025-09-15 02:24:07 -05:00
dependabot[bot]
aaf5ad23e2 Bump pypa/gh-action-pypi-publish in /.github/workflows
Bumps [pypa/gh-action-pypi-publish](https://github.com/pypa/gh-action-pypi-publish) from 1.12.3 to 1.13.0.
- [Release notes](https://github.com/pypa/gh-action-pypi-publish/releases)
- [Commits](https://github.com/pypa/gh-action-pypi-publish/compare/v1.12.3...v1.13.0)

---
updated-dependencies:
- dependency-name: pypa/gh-action-pypi-publish
  dependency-version: 1.13.0
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-04 15:36:31 +00:00
Sudo-Ivan
ce1b1dad7d Update Docker volume path 2025-08-18 03:17:23 -05:00
Sudo-Ivan
67ebc7e556 Fix formatting issues in test_client.py and test_client2.py for consistency in lambda function parameters. 2025-08-04 17:24:21 -05:00
Sudo-Ivan
b31fb748b8 Add node address to output and Fix formatting issues 2025-08-04 17:24:15 -05:00
Sudo-Ivan
eb27326763 Bump package version to 1.0.0. 2025-07-14 17:31:45 -05:00
Sudo-Ivan
f40d5a51ae Refactor main to improve readability and maintainability. 2025-07-14 17:27:17 -05:00
Sudo-Ivan
4aa83a2dfb Add badges to README.md. 2025-07-14 17:22:26 -05:00
23 changed files with 1247 additions and 2049 deletions

View File

@@ -15,13 +15,13 @@ jobs:
contents: read contents: read
strategy: strategy:
matrix: matrix:
python-version: ["3.10", "3.11", "3.12", "3.13"] python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- name: Set up Python ${{ matrix.python-version }} - name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5 uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
with: with:
python-version: ${{ matrix.python-version }} python-version: ${{ matrix.python-version }}
- name: Build Docker Image - name: Build Docker Image
run: docker build . --file Dockerfile --build-arg PYTHON_VERSION=${{ matrix.python-version }} --tag lxmfy-test:${{ matrix.python-version }} run: docker build . --file docker/Dockerfile --build-arg PYTHON_VERSION=${{ matrix.python-version }} --tag lxmfy-test:${{ matrix.python-version }}

View File

@@ -20,18 +20,18 @@ jobs:
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v4 uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- name: Set up QEMU - name: Set up QEMU
uses: docker/setup-qemu-action@v3 uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392
with: with:
platforms: amd64,arm64 platforms: amd64,arm64
- name: Set up Docker Buildx - name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3 uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435
- name: Log in to the Container registry - name: Log in to the Container registry
uses: docker/login-action@v3 uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1
with: with:
registry: ${{ env.REGISTRY }} registry: ${{ env.REGISTRY }}
username: ${{ github.actor }} username: ${{ github.actor }}
@@ -39,7 +39,7 @@ jobs:
- name: Extract metadata (tags, labels) for Docker - name: Extract metadata (tags, labels) for Docker
id: meta id: meta
uses: docker/metadata-action@v5 uses: docker/metadata-action@c1e51972afc2121e065aed6d45c65596fe445f3f
with: with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: | tags: |
@@ -51,9 +51,10 @@ jobs:
type=sha,format=short type=sha,format=short
- name: Build and push Docker image - name: Build and push Docker image
uses: docker/build-push-action@v5 uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
with: with:
context: . context: .
file: ./docker/Dockerfile
platforms: linux/amd64,linux/arm64 platforms: linux/amd64,linux/arm64
push: ${{ github.event_name != 'pull_request' }} push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }} tags: ${{ steps.meta.outputs.tags }}
@@ -63,7 +64,7 @@ jobs:
- name: Extract metadata (tags, labels) for Docker (rootless) - name: Extract metadata (tags, labels) for Docker (rootless)
id: meta_rootless id: meta_rootless
uses: docker/metadata-action@v5 uses: docker/metadata-action@c1e51972afc2121e065aed6d45c65596fe445f3f
with: with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-rootless images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-rootless
tags: | tags: |
@@ -74,10 +75,10 @@ jobs:
type=sha,format=short,suffix=-rootless type=sha,format=short,suffix=-rootless
- name: Build and push rootless Docker image - name: Build and push rootless Docker image
uses: docker/build-push-action@v5 uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
with: with:
context: . context: .
file: ./Dockerfile.rootless file: ./docker/Dockerfile.rootless
platforms: linux/amd64,linux/arm64 platforms: linux/amd64,linux/arm64
push: ${{ github.event_name != 'pull_request' }} push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta_rootless.outputs.tags }} tags: ${{ steps.meta_rootless.outputs.tags }}

View File

@@ -1,5 +1,14 @@
name: Publish Python 🐍 distribution 📦 to PyPI name: Publish Python 🐍 distribution 📦 to PyPI
# This workflow creates immutable releases:
# 1. Build packages
# 2. Publish to PyPI (only on tag push)
# 3. After successful PyPI publish:
# - Sign artifacts
# - Check if GitHub release exists (idempotent)
# - Create release with all artifacts atomically
# This ensures releases cannot be modified once published.
on: on:
push: push:
tags: tags:
@@ -23,11 +32,11 @@ jobs:
contents: read contents: read
id-token: write id-token: write
steps: steps:
- uses: actions/checkout@v4.2.2 - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
with: with:
persist-credentials: false persist-credentials: false
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v5.3.0 uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
with: with:
python-version: "3.13" python-version: "3.13"
- name: Install pypa/build - name: Install pypa/build
@@ -35,7 +44,7 @@ jobs:
- name: Build a binary wheel and a source tarball - name: Build a binary wheel and a source tarball
run: python3 -m build run: python3 -m build
- name: Store the distribution packages - name: Store the distribution packages
uses: actions/upload-artifact@v4.5.0 uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with: with:
name: python-package-distributions name: python-package-distributions
path: dist/ path: dist/
@@ -55,12 +64,12 @@ jobs:
steps: steps:
- name: Download all the dists - name: Download all the dists
uses: actions/download-artifact@v4.1.8 uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
with: with:
name: python-package-distributions name: python-package-distributions
path: dist/ path: dist/
- name: Publish distribution 📦 to PyPI - name: Publish distribution 📦 to PyPI
uses: pypa/gh-action-pypi-publish@v1.12.3 uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e
github-release: github-release:
name: Sign the Python 🐍 distribution 📦 and create GitHub Release name: Sign the Python 🐍 distribution 📦 and create GitHub Release
@@ -73,28 +82,37 @@ jobs:
steps: steps:
- name: Download all the dists - name: Download all the dists
uses: actions/download-artifact@v4.1.8 uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
with: with:
name: python-package-distributions name: python-package-distributions
path: dist/ path: dist/
- name: Sign the dists with Sigstore - name: Sign the dists with Sigstore
uses: sigstore/gh-action-sigstore-python@v3.0.0 uses: sigstore/gh-action-sigstore-python@f7ad0af51a5648d09a20d00370f0a91c3bdf8f84 # v3.0.1
with: with:
inputs: >- inputs: >-
./dist/*.tar.gz ./dist/*.tar.gz
./dist/*.whl ./dist/*.whl
- name: Create GitHub Release - name: Check if release exists
id: check_release
env:
GITHUB_TOKEN: ${{ github.token }}
run: |
if gh release view "$GITHUB_REF_NAME" --repo "$GITHUB_REPOSITORY" >/dev/null 2>&1; then
echo "exists=true" >> $GITHUB_OUTPUT
echo "Release $GITHUB_REF_NAME already exists, skipping creation"
else
echo "exists=false" >> $GITHUB_OUTPUT
echo "Release $GITHUB_REF_NAME does not exist, will create"
fi
continue-on-error: true
- name: Create GitHub Release with artifacts
if: steps.check_release.outputs.exists != 'true'
env: env:
GITHUB_TOKEN: ${{ github.token }} GITHUB_TOKEN: ${{ github.token }}
run: >- run: >-
gh release create gh release create
"$GITHUB_REF_NAME" "$GITHUB_REF_NAME"
--repo "$GITHUB_REPOSITORY" --repo "$GITHUB_REPOSITORY"
--notes "" --title "Release $GITHUB_REF_NAME"
- name: Upload artifact signatures to GitHub Release --notes "PyPI: https://pypi.org/project/rns-page-node/$GITHUB_REF_NAME/"
env: dist/*
GITHUB_TOKEN: ${{ github.token }}
run: >-
gh release upload
"$GITHUB_REF_NAME" dist/**
--repo "$GITHUB_REPOSITORY"

17
.github/workflows/safety.yml vendored Normal file
View File

@@ -0,0 +1,17 @@
name: Safety
on:
push:
branches: [ main ]
schedule:
- cron: '0 0 * * 0' # weekly
jobs:
security:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@main
- name: Run Safety CLI to check for vulnerabilities
uses: pyupio/safety-action@7baf6605473beffc874c1313ddf2db085c0cacf2 # v1
with:
api-key: ${{ secrets.SAFETY_API_KEY }}

49
.github/workflows/tests.yml vendored Normal file
View File

@@ -0,0 +1,49 @@
name: Run Tests
on:
push:
branches:
- main
pull_request:
branches:
- main
defaults:
run:
shell: bash
jobs:
test:
runs-on: ${{ matrix.os }}
permissions:
contents: read
strategy:
matrix:
os: ["ubuntu-latest", "windows-latest"]
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -e .
- name: Run tests
run: |
cd tests
chmod +x run_tests.sh
timeout 120 ./run_tests.sh
- name: Upload test logs on failure
if: failure()
uses: actions/upload-artifact@v4
with:
name: test-logs-${{ matrix.os }}-${{ matrix.python-version }}
path: tests/node.log

9
.gitignore vendored
View File

@@ -3,3 +3,12 @@ node-config/
files/ files/
.ruff_cache/ .ruff_cache/
__pycache__/ __pycache__/
dist/
*.egg-info/
.ruff_cache/
.venv/
.env
.env.local
.env.development.local
.env.test.local
.env.production.local

View File

@@ -2,19 +2,20 @@
# Detect if docker buildx is available # Detect if docker buildx is available
DOCKER_BUILD := $(shell docker buildx version >/dev/null 2>&1 && echo "docker buildx build" || echo "docker build") DOCKER_BUILD := $(shell docker buildx version >/dev/null 2>&1 && echo "docker buildx build" || echo "docker build")
DOCKER_BUILD_LOAD := $(shell docker buildx version >/dev/null 2>&1 && echo "docker buildx build --load" || echo "docker build")
.PHONY: all build sdist wheel clean install lint format docker-wheels docker-build docker-run docker-build-rootless docker-run-rootless help test docker-test .PHONY: all build sdist wheel clean install lint format docker-wheels docker-build docker-run docker-build-rootless docker-run-rootless help test docker-test
all: build all: build
build: clean build: clean
python3 setup.py sdist bdist_wheel python3 -m build
sdist: sdist:
python3 setup.py sdist python3 -m build --sdist
wheel: wheel:
python3 setup.py bdist_wheel python3 -m build --wheel
clean: clean:
rm -rf build dist *.egg-info rm -rf build dist *.egg-info
@@ -29,13 +30,13 @@ format:
ruff check --fix . ruff check --fix .
docker-wheels: docker-wheels:
$(DOCKER_BUILD) --target builder -f Dockerfile.build -t rns-page-node-builder . $(DOCKER_BUILD) --target builder -f docker/Dockerfile.build -t rns-page-node-builder .
docker create --name builder-container rns-page-node-builder true docker create --name builder-container rns-page-node-builder true
docker cp builder-container:/src/dist ./dist docker cp builder-container:/src/dist ./dist
docker rm builder-container docker rm builder-container
docker-build: docker-build:
$(DOCKER_BUILD) $(BUILD_ARGS) -f Dockerfile -t rns-page-node:latest . $(DOCKER_BUILD_LOAD) $(BUILD_ARGS) -f docker/Dockerfile -t rns-page-node:latest .
docker-run: docker-run:
docker run --rm -it \ docker run --rm -it \
@@ -50,7 +51,7 @@ docker-run:
--announce-interval 360 --announce-interval 360
docker-build-rootless: docker-build-rootless:
$(DOCKER_BUILD) $(BUILD_ARGS) -f Dockerfile.rootless -t rns-page-node-rootless:latest . $(DOCKER_BUILD_LOAD) $(BUILD_ARGS) -f docker/Dockerfile.rootless -t rns-page-node-rootless:latest .
docker-run-rootless: docker-run-rootless:
docker run --rm -it \ docker run --rm -it \
@@ -68,7 +69,7 @@ test:
bash tests/run_tests.sh bash tests/run_tests.sh
docker-test: docker-test:
$(DOCKER_BUILD) -f tests/Dockerfile.tests -t rns-page-node-tests . $(DOCKER_BUILD_LOAD) -f docker/Dockerfile.tests -t rns-page-node-tests .
docker run --rm rns-page-node-tests docker run --rm rns-page-node-tests
help: help:

127
README.md
View File

@@ -1,27 +1,82 @@
# RNS Page Node # RNS Page Node
[Русская](README.ru.md)
[![Build and Publish Docker Image](https://github.com/Sudo-Ivan/rns-page-node/actions/workflows/docker.yml/badge.svg)](https://github.com/Sudo-Ivan/rns-page-node/actions/workflows/docker.yml)
[![Docker Build Test](https://github.com/Sudo-Ivan/rns-page-node/actions/workflows/docker-test.yml/badge.svg)](https://github.com/Sudo-Ivan/rns-page-node/actions/workflows/docker-test.yml)
[![DeepSource](https://app.deepsource.com/gh/Sudo-Ivan/rns-page-node.svg/?label=active+issues&show_trend=true&token=kajzd0SjJXSzkuN3z3kG9gQw)](https://app.deepsource.com/gh/Sudo-Ivan/rns-page-node/)
A simple way to serve pages and files over the [Reticulum network](https://reticulum.network/). Drop-in replacement for [NomadNet](https://github.com/markqvist/NomadNet) nodes that primarily serve pages and files. A simple way to serve pages and files over the [Reticulum network](https://reticulum.network/). Drop-in replacement for [NomadNet](https://github.com/markqvist/NomadNet) nodes that primarily serve pages and files.
## Features
- Serves pages and files over RNS
- Dynamic page support with environment variables
- Form data and request parameter parsing
## Usage ## Usage
```bash ```bash
# Pip
# May require --break-system-packages
pip install rns-page-node pip install rns-page-node
# Pipx
pipx install rns-page-node
# uv
uv venv
source .venv/bin/activate
uv pip install rns-page-node
# Pipx via Git
pipx install git+https://github.com/Sudo-Ivan/rns-page-node.git
``` ```
## Usage
```bash ```bash
# will use current directory for pages and files
rns-page-node rns-page-node
``` ```
## Usage or with command-line options:
```bash ```bash
rns-page-node --node-name "Page Node" --pages-dir ./pages --files-dir ./files --identity-dir ./node-config --announce-interval 360 rns-page-node --node-name "Page Node" --pages-dir ./pages --files-dir ./files --identity-dir ./node-config --announce-interval 360
``` ```
or with a config file:
```bash
rns-page-node /path/to/config.conf
```
### Configuration File
You can use a configuration file to persist settings. See `config.example` for an example.
Config file format is simple `key=value` pairs:
```
# Comment lines start with #
node-name=My Page Node
pages-dir=./pages
files-dir=./files
identity-dir=./node-config
announce-interval=360
```
Priority order: Command-line arguments > Config file > Defaults
### Docker/Podman ### Docker/Podman
```bash ```bash
docker run -it --rm -v ./pages:/app/pages -v ./files:/app/files -v ./node-config:/app/node-config -v ./config:/app/config ghcr.io/sudo-ivan/rns-page-node:latest docker run -it --rm -v ./pages:/app/pages -v ./files:/app/files -v ./node-config:/app/node-config -v ./config:/root/.reticulum ghcr.io/sudo-ivan/rns-page-node:latest
``` ```
### Docker/Podman Rootless ### Docker/Podman Rootless
@@ -54,60 +109,30 @@ make docker-wheels
## Pages ## Pages
Supports Micron `.mu` and dynamic pages with `#!` in the micron files. Supports dynamic executable pages with full request data parsing. Pages can receive:
- Form fields via `field_*` environment variables
- Link variables via `var_*` environment variables
- Remote identity via `remote_identity` environment variable
- Link ID via `link_id` environment variable
## Statistics Tracking This enables forums, chats, and other interactive applications compatible with NomadNet clients.
The node now includes comprehensive statistics tracking for monitoring peer connections and page/file requests:
### Command Line Options for Stats
```bash
# Print stats every 60 seconds
rns-page-node --stats-interval 60
# Save stats to JSON file on shutdown
rns-page-node --save-stats node_stats.json
# Actively write stats to file (live updates)
rns-page-node --stats-file stats.json
# Combined: live stats file + periodic display + final save
rns-page-node --stats-file stats.json --stats-interval 300 --save-stats final_stats.json
```
### Docker Stats Usage
```bash
# With periodic stats display
docker run -it --rm -v ./pages:/app/pages -v ./files:/app/files -v ./node-config:/app/node-config -v ./config:/app/config ghcr.io/sudo-ivan/rns-page-node:latest --stats-interval 60
# Save stats to mounted volume
docker run -it --rm -v ./pages:/app/pages -v ./files:/app/files -v ./node-config:/app/node-config -v ./config:/app/config -v ./stats:/app/stats ghcr.io/sudo-ivan/rns-page-node:latest --save-stats /app/stats/node_stats.json
```
### Tracked Metrics
- **Connection Statistics**: Total connections, active connections, peer tracking
- **Request Statistics**: Page requests, file requests, requests by path and peer
- **Performance Metrics**: Requests per hour, uptime, response patterns
- **Historical Data**: Recent request history, hourly/daily aggregations
- **Top Content**: Most requested pages and files, most active peers
## Options ## Options
``` ```
-c, --config: The path to the Reticulum config file. Positional arguments:
-n, --node-name: The name of the node. node_config Path to rns-page-node config file
-p, --pages-dir: The directory to serve pages from.
-f, --files-dir: The directory to serve files from. Optional arguments:
-i, --identity-dir: The directory to persist the node's identity. -c, --config Path to the Reticulum config file
-a, --announce-interval: The interval to announce the node's presence. -n, --node-name Name of the node
--page-refresh-interval: The interval to refresh pages (seconds, 0 disables). -p, --pages-dir Directory to serve pages from
--file-refresh-interval: The interval to refresh files (seconds, 0 disables). -f, --files-dir Directory to serve files from
-l, --log-level: The logging level. -i, --identity-dir Directory to persist the node's identity
--stats-interval: Print stats every N seconds (0 disables). -a, --announce-interval Interval to announce the node's presence (in minutes, default: 360 = 6 hours)
--save-stats: Save stats to JSON file on shutdown. --page-refresh-interval Interval to refresh pages (in seconds, 0 = disabled)
--file-refresh-interval Interval to refresh files (in seconds, 0 = disabled)
-l, --log-level Logging level (DEBUG, INFO, WARNING, ERROR, CRITICAL)
``` ```
## License ## License

124
README.ru.md Normal file
View File

@@ -0,0 +1,124 @@
# RNS Page Node
[English](README.md)
Простой способ для раздачи страниц и файлов через сеть [Reticulum](https://reticulum.network/). Прямая замена для узлов [NomadNet](https://github.com/markqvist/NomadNet), которые в основном служат для раздачи страниц и файлов.
## Особенности
- Раздача страниц и файлов через RNS
- Поддержка динамических страниц с переменными окружения
- Разбор данных форм и параметров запросов
## Установка
```bash
# Pip
# Может потребоваться --break-system-packages
pip install rns-page-node
# Pipx
pipx install rns-page-node
# uv
uv venv
source .venv/bin/activate
uv pip install rns-page-node
# Pipx через Git
pipx install git+https://github.com/Sudo-Ivan/rns-page-node.git
```
## Использование
```bash
# будет использовать текущий каталог для страниц и файлов
rns-page-node
```
или с параметрами командной строки:
```bash
rns-page-node --node-name "Page Node" --pages-dir ./pages --files-dir ./files --identity-dir ./node-config --announce-interval 360
```
или с файлом конфигурации:
```bash
rns-page-node /путь/к/config.conf
```
### Файл Конфигурации
Вы можете использовать файл конфигурации для сохранения настроек. См. `config.example` для примера.
Формат файла конфигурации - простые пары `ключ=значение`:
```
# Строки комментариев начинаются с #
node-name=Мой Page Node
pages-dir=./pages
files-dir=./files
identity-dir=./node-config
announce-interval=360
```
Порядок приоритета: Аргументы командной строки > Файл конфигурации > Значения по умолчанию
### Docker/Podman
```bash
docker run -it --rm -v ./pages:/app/pages -v ./files:/app/files -v ./node-config:/app/node-config -v ./config:/root/.reticulum ghcr.io/sudo-ivan/rns-page-node:latest
```
### Docker/Podman без root-доступа
```bash
mkdir -p ./pages ./files ./node-config ./config
chown -R 1000:1000 ./pages ./files ./node-config ./config
podman run -it --rm -v ./pages:/app/pages -v ./files:/app/files -v ./node-config:/app/node-config -v ./config:/app/config ghcr.io/sudo-ivan/rns-page-node:latest-rootless
```
Монтирование томов необязательно, вы также можете скопировать страницы и файлы в контейнер с помощью `podman cp` или `docker cp`.
## Сборка
```bash
make build
```
Сборка wheels:
```bash
make wheel
```
### Сборка Wheels в Docker
```bash
make docker-wheels
```
## Страницы
Поддержка динамических исполняемых страниц с полным разбором данных запросов. Страницы могут получать:
- Поля форм через переменные окружения `field_*`
- Переменные ссылок через переменные окружения `var_*`
- Удаленную идентификацию через переменную окружения `remote_identity`
- ID соединения через переменную окружения `link_id`
Это позволяет создавать форумы, чаты и другие интерактивные приложения, совместимые с клиентами NomadNet.
## Параметры
```
Позиционные аргументы:
node_config Путь к файлу конфигурации rns-page-node
Необязательные аргументы:
-c, --config Путь к файлу конфигурации Reticulum
-n, --node-name Имя узла
-p, --pages-dir Каталог для раздачи страниц
-f, --files-dir Каталог для раздачи файлов
-i, --identity-dir Каталог для сохранения идентификационных данных узла
-a, --announce-interval Интервал анонсирования присутствия узла (в минутах, по умолчанию: 360 = 6 часов)
--page-refresh-interval Интервал обновления страниц (в секундах, 0 = отключено)
--file-refresh-interval Интервал обновления файлов (в секундах, 0 = отключено)
-l, --log-level Уровень логирования (DEBUG, INFO, WARNING, ERROR, CRITICAL)
```
## Лицензия
Этот проект включает части кодовой базы [NomadNet](https://github.com/markqvist/NomadNet), которая лицензирована под GNU General Public License v3.0 (GPL-3.0). Как производная работа, этот проект также распространяется на условиях GPL-3.0. Полный текст лицензии смотрите в файле [LICENSE](LICENSE).

31
config.example Normal file
View File

@@ -0,0 +1,31 @@
# rns-page-node configuration file
# Lines starting with # are comments
# Format: key=value
# Reticulum config directory path
# reticulum-config=/path/to/reticulum/config
# Node display name
node-name=My Page Node
# Pages directory
pages-dir=./pages
# Files directory
files-dir=./files
# Node identity directory
identity-dir=./node-config
# Announce interval in minutes (default: 360 = 6 hours)
announce-interval=360
# Page refresh interval in seconds (0 = disabled)
page-refresh-interval=300
# File refresh interval in seconds (0 = disabled)
file-refresh-interval=300
# Log level (DEBUG, INFO, WARNING, ERROR, CRITICAL)
log-level=INFO

View File

1543
poetry.lock generated
View File

File diff suppressed because it is too large Load Diff

View File

@@ -1,16 +1,25 @@
[project] [project]
name = "rns-page-node" name = "rns-page-node"
version = "0.2.0" version = "1.3.0"
license = "GPL-3.0-only" license = "GPL-3.0-only"
description = "A simple way to serve pages and files over the Reticulum network." description = "A simple way to serve pages and files over the Reticulum network."
authors = [ authors = [
{name = "Sudo-Ivan"} {name = "Sudo-Ivan"}
] ]
readme = "README.md" readme = "README.md"
requires-python = ">=3.10" requires-python = ">3.9.0,<3.9.1 || >3.9.1"
dependencies = [ dependencies = [
"rns (>=1.0.0,<1.5.0)" "rns (>=1.0.4,<1.5.0)",
"cryptography>=46.0.3"
] ]
classifiers = [
"Programming Language :: Python :: 3",
"Operating System :: OS Independent",
]
[project.urls]
Homepage = "https://github.com/Sudo-Ivan/rns-page-node"
Repository = "https://github.com/Sudo-Ivan/rns-page-node"
[project.scripts] [project.scripts]
rns-page-node = "rns_page_node.main:main" rns-page-node = "rns_page_node.main:main"
@@ -20,6 +29,4 @@ requires = ["poetry-core>=2.0.0,<3.0.0"]
build-backend = "poetry.core.masonry.api" build-backend = "poetry.core.masonry.api"
[tool.poetry.group.dev.dependencies] [tool.poetry.group.dev.dependencies]
ruff = "^0.12.3" ruff = "^0.13.3"
safety = "^3.6.0"

View File

@@ -1 +1 @@
rns=1.0.0 rns=1.0.4

View File

@@ -1,2 +1,6 @@
# rns_page_node package """RNS Page Node package.
__all__ = ['main']
A minimal Reticulum page node that serves .mu pages and files over RNS.
"""
__all__ = ["main"]

View File

File diff suppressed because it is too large Load Diff

View File

@@ -1,31 +0,0 @@
from setuptools import setup, find_packages
with open('README.md', 'r', encoding='utf-8') as fh:
long_description = fh.read()
setup(
name='rns-page-node',
version='0.2.0',
author='Sudo-Ivan',
author_email='',
description='A simple way to serve pages and files over the Reticulum network.',
long_description=long_description,
long_description_content_type='text/markdown',
url='https://github.com/Sudo-Ivan/rns-page-node',
packages=find_packages(),
license="GPL-3.0",
python_requires='>=3.10',
install_requires=[
'rns>=1.0.0,<1.5.0',
],
entry_points={
'console_scripts': [
'rns-page-node=rns_page_node.main:main',
],
},
classifiers=[
'Programming Language :: Python :: 3',
'License :: OSI Approved :: GNU General Public License v3 (GPLv3)',
'Operating System :: OS Independent',
],
)

28
tests/run_tests.sh Normal file → Executable file
View File

@@ -9,11 +9,33 @@ rm -rf config node-config pages files node.log
mkdir -p config node-config pages files mkdir -p config node-config pages files
# Create a sample page and a test file # Create a sample page and a test file
cat > pages/index.mu << EOF cat > pages/index.mu << 'EOF'
>Test Page #!/usr/bin/env python3
This is a test page. import os
print("`F0f0`_`Test Page`_")
print("This is a test page with environment variable support.")
print()
print("`F0f0`_`Environment Variables`_")
params = []
for key, value in os.environ.items():
if key.startswith(('field_', 'var_')):
params.append(f"- `Faaa`{key}`f: `F0f0`{value}`f")
if params:
print("\n".join(params))
else:
print("- No parameters received")
print()
print("`F0f0`_`Remote Identity`_")
remote_id = os.environ.get('remote_identity', '33aff86b736acd47dca07e84630fd192') # Mock for testing
print(f"`Faaa`{remote_id}`f")
EOF EOF
chmod +x pages/index.mu
cat > files/text.txt << EOF cat > files/text.txt << EOF
This is a test file. This is a test file.
EOF EOF

View File

@@ -1,20 +1,21 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import os import os
import sys import sys
import time
import threading import threading
import time
import RNS import RNS
# Determine base directory for tests # Determine base directory for tests
dir_path = os.path.abspath(os.path.dirname(__file__)) dir_path = os.path.abspath(os.path.dirname(__file__))
config_dir = os.path.join(dir_path, 'config') config_dir = os.path.join(dir_path, "config")
identity_dir = os.path.join(dir_path, 'node-config') identity_dir = os.path.join(dir_path, "node-config")
# Initialize Reticulum with shared config # Initialize Reticulum with shared config
RNS.Reticulum(config_dir) RNS.Reticulum(config_dir)
# Load server identity (created by the page node) # Load server identity (created by the page node)
identity_file = os.path.join(identity_dir, 'identity') identity_file = os.path.join(identity_dir, "identity")
server_identity = RNS.Identity.from_file(identity_file) server_identity = RNS.Identity.from_file(identity_file)
# Create a destination to the server node # Create a destination to the server node
@@ -22,8 +23,8 @@ destination = RNS.Destination(
server_identity, server_identity,
RNS.Destination.OUT, RNS.Destination.OUT,
RNS.Destination.SINGLE, RNS.Destination.SINGLE,
'nomadnetwork', "nomadnetwork",
'node' "node",
) )
# Ensure we know a path to the destination # Ensure we know a path to the destination
@@ -39,66 +40,190 @@ global_link = RNS.Link(destination)
responses = {} responses = {}
done_event = threading.Event() done_event = threading.Event()
# Test data for environment variables
test_data_dict = {
"var_field_test": "dictionary_value",
"var_field_message": "hello_world",
"var_action": "test_action",
}
test_data_dict2 = {
"field_username": "testuser",
"field_message": "hello_from_form",
"var_action": "submit",
}
# Callback for page response # Callback for page response
def on_page(response): def on_page(response):
data = response.response data = response.response
if isinstance(data, bytes): if isinstance(data, bytes):
text = data.decode('utf-8') text = data.decode("utf-8")
else: else:
text = str(data) text = str(data)
print('Received page:') print("Received page (no data):")
print(text) print(text)
responses['page'] = text responses["page"] = text
if 'file' in responses: check_responses()
# Callback for page response with dictionary data
def on_page_dict(response):
data = response.response
if isinstance(data, bytes):
text = data.decode("utf-8")
else:
text = str(data)
print("Received page (dict data):")
print(text)
responses["page_dict"] = text
check_responses()
# Callback for page response with second dict data
def on_page_dict2(response):
data = response.response
if isinstance(data, bytes):
text = data.decode("utf-8")
else:
text = str(data)
print("Received page (dict2 data):")
print(text)
responses["page_dict2"] = text
check_responses()
def check_responses():
if (
"page" in responses
and "page_dict" in responses
and "page_dict2" in responses
and "file" in responses
):
done_event.set() done_event.set()
# Callback for file response # Callback for file response
def on_file(response): def on_file(response):
data = response.response data = response.response
# Handle response as [fileobj, headers] # Handle response as [fileobj, headers]
if isinstance(data, list) and len(data) == 2 and hasattr(data[0], 'read'): if isinstance(data, list) and len(data) == 2 and hasattr(data[0], "read"):
fileobj, headers = data fileobj, headers = data
file_data = fileobj.read() file_data = fileobj.read()
filename = headers.get(b'name', b'').decode('utf-8') filename = headers.get(b"name", b"").decode("utf-8")
print(f'Received file ({filename}):') print(f"Received file ({filename}):")
print(file_data.decode('utf-8')) print(file_data.decode("utf-8"))
responses['file'] = file_data.decode('utf-8') responses["file"] = file_data.decode("utf-8")
# Handle response as a raw file object # Handle response as a raw file object
elif hasattr(data, 'read'): elif hasattr(data, "read"):
file_data = data.read() file_data = data.read()
filename = os.path.basename('text.txt') filename = os.path.basename("text.txt")
print(f'Received file ({filename}):') print(f"Received file ({filename}):")
print(file_data.decode('utf-8')) print(file_data.decode("utf-8"))
responses['file'] = file_data.decode('utf-8') responses["file"] = file_data.decode("utf-8")
# Handle response as raw bytes # Handle response as raw bytes
elif isinstance(data, bytes): elif isinstance(data, bytes):
text = data.decode('utf-8') text = data.decode("utf-8")
print('Received file:') print("Received file:")
print(text) print(text)
responses['file'] = text responses["file"] = text
else: else:
print('Received file (unhandled format):', data) print("Received file (unhandled format):", data)
responses['file'] = str(data) responses["file"] = str(data)
if 'page' in responses: check_responses()
done_event.set()
# Request the page and file once the link is established
# Request the pages and file once the link is established
def on_link_established(link): def on_link_established(link):
link.request('/page/index.mu', None, response_callback=on_page) # Test page without data
link.request('/file/text.txt', None, response_callback=on_file) link.request("/page/index.mu", None, response_callback=on_page)
# Test page with dictionary data (simulates var_ prefixed data)
link.request("/page/index.mu", test_data_dict, response_callback=on_page_dict)
# Test page with form field data (simulates field_ prefixed data)
link.request("/page/index.mu", test_data_dict2, response_callback=on_page_dict2)
# Test file serving
link.request("/file/text.txt", None, response_callback=on_file)
# Register callbacks # Register callbacks
global_link.set_link_established_callback(on_link_established) global_link.set_link_established_callback(on_link_established)
global_link.set_link_closed_callback(lambda l: done_event.set()) global_link.set_link_closed_callback(lambda link: done_event.set())
# Wait for responses or timeout # Wait for responses or timeout
if not done_event.wait(timeout=30): if not done_event.wait(timeout=30):
print('Test timed out.', file=sys.stderr) print("Test timed out.", file=sys.stderr)
sys.exit(1) sys.exit(1)
if responses.get('page') and responses.get('file'):
print('Tests passed!') # Validate test results
def validate_test_results():
"""Validate that all responses contain expected content"""
# Check basic page response (no data)
if "page" not in responses:
print("ERROR: No basic page response received", file=sys.stderr)
return False
page_content = responses["page"]
if "No parameters received" not in page_content:
print("ERROR: Basic page should show 'No parameters received'", file=sys.stderr)
return False
if "33aff86b736acd47dca07e84630fd192" not in page_content:
print("ERROR: Basic page should show mock remote identity", file=sys.stderr)
return False
# Check page with dictionary data
if "page_dict" not in responses:
print("ERROR: No dictionary data page response received", file=sys.stderr)
return False
dict_content = responses["page_dict"]
if "var_field_test" not in dict_content or "dictionary_value" not in dict_content:
print(
"ERROR: Dictionary data page should contain processed environment variables",
file=sys.stderr,
)
return False
if "33aff86b736acd47dca07e84630fd192" not in dict_content:
print(
"ERROR: Dictionary data page should show mock remote identity",
file=sys.stderr,
)
return False
# Check page with second dictionary data (form fields)
if "page_dict2" not in responses:
print("ERROR: No dict2 data page response received", file=sys.stderr)
return False
dict2_content = responses["page_dict2"]
if "field_username" not in dict2_content or "testuser" not in dict2_content:
print(
"ERROR: Dict2 data page should contain processed environment variables",
file=sys.stderr,
)
return False
if "33aff86b736acd47dca07e84630fd192" not in dict2_content:
print(
"ERROR: Dict2 data page should show mock remote identity",
file=sys.stderr,
)
return False
# Check file response
if "file" not in responses:
print("ERROR: No file response received", file=sys.stderr)
return False
file_content = responses["file"]
if "This is a test file" not in file_content:
print("ERROR: File content doesn't match expected content", file=sys.stderr)
return False
return True
if validate_test_results():
print("All tests passed! Environment variable processing works correctly.")
sys.exit(0) sys.exit(0)
else: else:
print('Tests failed.', file=sys.stderr) print("Tests failed.", file=sys.stderr)
sys.exit(1) sys.exit(1)

View File

@@ -1,20 +1,26 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import os import os
import sys import sys
import time
import threading import threading
import time
import RNS import RNS
dir_path = os.path.abspath(os.path.dirname(__file__)) dir_path = os.path.abspath(os.path.dirname(__file__))
config_dir = os.path.join(dir_path, 'config') config_dir = os.path.join(dir_path, "config")
RNS.Reticulum(config_dir) RNS.Reticulum(config_dir)
DESTINATION_HEX = '49b2d959db8528347d0a38083aec1042' # Ivans Node that runs rns-page-node DESTINATION_HEX = (
"49b2d959db8528347d0a38083aec1042" # Ivans Node that runs rns-page-node
)
dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH // 8) * 2 dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH // 8) * 2
if len(DESTINATION_HEX) != dest_len: if len(DESTINATION_HEX) != dest_len:
print(f"Invalid destination length (got {len(DESTINATION_HEX)}, expected {dest_len})", file=sys.stderr) print(
f"Invalid destination length (got {len(DESTINATION_HEX)}, expected {dest_len})",
file=sys.stderr,
)
sys.exit(1) sys.exit(1)
destination_hash = bytes.fromhex(DESTINATION_HEX) destination_hash = bytes.fromhex(DESTINATION_HEX)
@@ -31,29 +37,33 @@ destination = RNS.Destination(
server_identity, server_identity,
RNS.Destination.OUT, RNS.Destination.OUT,
RNS.Destination.SINGLE, RNS.Destination.SINGLE,
'nomadnetwork', "nomadnetwork",
'node' "node",
) )
link = RNS.Link(destination) link = RNS.Link(destination)
done_event = threading.Event() done_event = threading.Event()
def on_page(response): def on_page(response):
data = response.response data = response.response
if isinstance(data, bytes): if isinstance(data, bytes):
text = data.decode('utf-8') text = data.decode("utf-8")
else: else:
text = str(data) text = str(data)
print('Fetched page content:') print("Fetched page content:")
print(text) print(text)
done_event.set() done_event.set()
link.set_link_established_callback(lambda l: l.request('/page/index.mu', None, response_callback=on_page))
link.set_link_closed_callback(lambda l: done_event.set()) link.set_link_established_callback(
lambda link: link.request("/page/index.mu", None, response_callback=on_page),
)
link.set_link_closed_callback(lambda link: done_event.set())
if not done_event.wait(timeout=30): if not done_event.wait(timeout=30):
print('Timed out waiting for page', file=sys.stderr) print("Timed out waiting for page", file=sys.stderr)
sys.exit(1) sys.exit(1)
print('Done fetching page.') print("Done fetching page.")
sys.exit(0) sys.exit(0)