{"id":46469541,"url":"https://github.com/kscardinal/loto-report-generator","last_synced_at":"2026-03-06T06:03:46.103Z","repository":{"id":313035408,"uuid":"976834647","full_name":"kscardinal/loto-report-generator","owner":"kscardinal","description":"A python application for generating PDF reports related to lockout/tagout procedures with custom formatting, images, and fonts.","archived":false,"fork":false,"pushed_at":"2025-12-05T18:27:34.000Z","size":9422,"stargazers_count":1,"open_issues_count":16,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-12-09T01:13:19.835Z","etag":null,"topics":["digitalocean","fastapi","html","python","reportlab-pdf","server"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kscardinal.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-05-02T20:52:27.000Z","updated_at":"2025-12-05T18:27:38.000Z","dependencies_parsed_at":"2025-09-03T15:24:58.185Z","dependency_job_id":"8bc1ee48-a601-47c8-a4a8-36c9700ec76f","html_url":"https://github.com/kscardinal/loto-report-generator","commit_stats":null,"previous_names":["kscardinal/loto-report-generator"],"tags_count":21,"template":false,"template_full_name":null,"purl":"pkg:github/kscardinal/loto-report-generator","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kscardinal%2Floto-report-generator","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kscardinal%2Floto-report-generator/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kscardinal%2Floto-report-generator/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kscardinal%2Floto-report-generator/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kscardinal","download_url":"https://codeload.github.com/kscardinal/loto-report-generator/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kscardinal%2Floto-report-generator/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30164532,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-06T04:43:31.446Z","status":"ssl_error","status_checked_at":"2026-03-06T04:40:30.133Z","response_time":250,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["digitalocean","fastapi","html","python","reportlab-pdf","server"],"created_at":"2026-03-06T06:03:28.610Z","updated_at":"2026-03-06T06:03:45.039Z","avatar_url":"https://github.com/kscardinal.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv style=\"text-align: center;\"\u003e \n\t\u003ch1 align=\"center\"\u003e📌 loto-report-generator\u003c/h1\u003e \n\t\u003cp align=\"center\"\u003e\u003ccode\u003eloto-report-generator\u003c/code\u003e is a Python web application for generating, managing, and downloading LOTO (Lockout/Tagout) reports. It supports PDF generation, JSON/photo uploads, audit logging, and role-based access, all configurable via environment variables and Docker.\u003c/p\u003e \n\u003c/div\u003e\n\n\u003cdiv style=\"text-align: center;\"\u003e\n\t\u003cp align=\"center\"\u003e\n\t\t\u003cimg src=\"https://img.shields.io/github/license/kscardinal/loto-report-generator?style=for-the-badge\u0026logo=git\u0026logoColor=white\u0026color=0080ff\" alt=\"GitHub License\"\u003e\n\t\t\u003cimg src=\"https://img.shields.io/github/v/release/kscardinal/loto-report-generator?style=for-the-badge\u0026logo=git\u0026logoColor=white\u0026color=0080ff\" alt=\"GitHub Release\"\u003e\n\t\t\u003cimg src=\"https://img.shields.io/github/commit-activity/t/kscardinal/loto-report-generator?style=for-the-badge\u0026logo=git\u0026logoColor=white\u0026color=0080ff\" alt=\"GitHub Commit Activity\"\u003e\n\t\t\u003cimg src=\"https://img.shields.io/github/last-commit/kscardinal/loto-report-generator?style=for-the-badge\u0026logo=git\u0026logoColor=white\u0026color=0080ff\" alt=\"GitHub Last Commit\"\u003e\n\t\t\u003cimg src=\"https://img.shields.io/github/contributors/kscardinal/loto-report-generator?style=for-the-badge\u0026logo=git\u0026logoColor=white\u0026color=0080ff\" alt=\"GitHub Contributors\"\u003e\n\t\t\u003cimg src=\"https://img.shields.io/github/repo-size/kscardinal/loto-report-generator?style=for-the-badge\u0026color=0080ff\"\u003e\n\t\u003c/p\u003e\n\u003c/div\u003e\n\n\u003cdiv style=\"text-align: center; padding-top: 0px\"\u003e\n\t\u003cp align=\"center\"\u003e\n\t\t\u003cimg src=\"https://img.shields.io/github/issues/kscardinal/loto-report-generator?style=for-the-badge\u0026color=4CAF50\"\u003e\n\t\t\u003cimg src=\"https://img.shields.io/github/issues-pr/kscardinal/loto-report-generator?style=for-the-badge\u0026color=4CAF50\"\u003e\n\t\u003c/p\u003e\n\u003c/div\u003e\n\n\u003cdiv style=\"text-align: center; padding-top: 0px\"\u003e\n\t\u003cp align=\"center\"\u003e\n\t\t\u003cimg src=\"https://img.shields.io/github/actions/workflow/status/kscardinal/loto-report-generator/pythonScriptTests.yml?label=Python%20Script%20Tests\u0026style=for-the-badge\u0026logo=github\u0026logoColor=white\u0026color=4CAF50\" alt=\"GitHub Actions Workflow Status\"\u003e\n\t\t\u003cimg src=\"https://img.shields.io/github/actions/workflow/status/kscardinal/loto-report-generator/serverEndpointTests.yml?label=Server%20Endpoint%20Tests\u0026style=for-the-badge\u0026logo=github\u0026logoColor=white\u0026color=4CAF50\" alt=\"GitHub Actions Workflow Status\"\u003e\n\t\u003c/p\u003e\n\u003c/div\u003e\n\n\u003cdiv style=\"text-align: center; padding-top: 0px\"\u003e\n\t\u003cp align=\"center\"\u003e\n\t\t\u003cimg src=\"https://img.shields.io/github/milestones/progress/kscardinal/loto-report-generator/1?style=for-the-badge\u0026color=0080ff\"\u003e\n\t\t\u003cimg src=\"https://img.shields.io/github/milestones/progress/kscardinal/loto-report-generator/2?style=for-the-badge\u0026color=0080ff\"\u003e\n\t\t\u003cimg src=\"https://img.shields.io/github/milestones/progress/kscardinal/loto-report-generator/3?style=for-the-badge\u0026color=0080ff\"\u003e\n\t\u003c/p\u003e\n\u003c/div\u003e\n\n---\n\n\u003cdiv style=\"text-align: center; padding-top: 30px\"\u003e\n\t\u003cp align=\"center\"\u003e\n\t\t\u003cimg src=\"https://img.shields.io/badge/python-3776AB.svg?style=for-the-badge\u0026logo=python\u0026logoColor=white\" alt=\"Python Badge\"\u003e\n\t\t\u003cimg src=\"https://img.shields.io/badge/JavaScript-F7DF1E.svg?style=for-the-badge\u0026logo=javascript\u0026logoColor=white\" alt=\"JavaScript Badge\"\u003e\n\t\t\u003cimg src=\"https://img.shields.io/badge/TypeScript-3178C6.svg?style=for-the-badge\u0026logo=typescript\u0026logoColor=white\" alt=\"TypeScript Badge\"\u003e\n\t\t\u003cimg src=\"https://img.shields.io/badge/FastAPI-009688.svg?style=for-the-badge\u0026logo=fastapi\u0026logoColor=white\" alt=\"FastAPI Badge\"\u003e\n\t\t\u003cimg src=\"https://img.shields.io/badge/NGINX-009639.svg?style=for-the-badge\u0026logo=nginx\u0026logoColor=white\" alt=\"NGINX Badge\"\u003e\n\t\t\u003cimg src=\"https://img.shields.io/badge/Jinja-7E0C1B.svg?style=for-the-badge\u0026logo=jinja\u0026logoColor=white\" alt=\"Jinja Badge\"\u003e\n\t\t\u003cimg src=\"https://img.shields.io/badge/JSON-000000.svg?style=for-the-badge\u0026logo=json\u0026logoColor=white\" alt=\"JSON Badge\"\u003e\n\t\t\u003cimg src=\"https://img.shields.io/badge/JWT-000000.svg?style=for-the-badge\u0026logo=jsonwebtokens\u0026logoColor=white\" alt=\"JWT Badge\"\u003e\n\t\t\u003cimg src=\"https://img.shields.io/badge/Markdown-000000.svg?style=for-the-badge\u0026logo=markdown\u0026logoColor=white\" alt=\"Markdown Badge\"\u003e\n\t\t\u003cimg src=\"https://img.shields.io/badge/HTML-E34F26.svg?style=for-the-badge\u0026logo=html5\u0026logoColor=white\" alt=\"HTML Badge\"\u003e\n\t\t\u003cimg src=\"https://img.shields.io/badge/CSS-663399.svg?style=for-the-badge\u0026logo=css\u0026logoColor=white\" alt=\"CSS Badge\"\u003e\n\t\t\u003cimg src=\"https://img.shields.io/badge/MongoDB-47A248.svg?style=for-the-badge\u0026logo=MongoDB\u0026logoColor=white\" alt=\"MongoDB Badge\"\u003e\n\t\t\u003cimg src=\"https://img.shields.io/badge/.env-ECD53F.svg?style=for-the-badge\u0026logo=.env\u0026logoColor=white\" alt=\".ENV Badge\"\u003e\n\t\t\u003cimg src=\"https://img.shields.io/badge/YAML-CB171E.svg?style=for-the-badge\u0026logo=YAML\u0026logoColor=white\" alt=\"YAML Badge\"\u003e\n\t\t\u003cimg src=\"https://img.shields.io/badge/Pytest-0A9EDC.svg?style=for-the-badge\u0026logo=Pytest\u0026logoColor=white\" alt=\"Pytest Badge\"\u003e\n\t  \t\u003cimg src=\"https://img.shields.io/badge/node.js-5FA04E.svg?style=for-the-badge\u0026logo=node.js\u0026logoColor=white\" alt=\"Node.js Badge\"\u003e\n\t\t\u003cimg src=\"https://img.shields.io/badge/UV-DE5FE9.svg?style=for-the-badge\u0026logo=UV\u0026logoColor=white\" alt=\"UV Badge\"\u003e\n\t\t\u003cimg src=\"https://img.shields.io/badge/docker-2496ED.svg?style=for-the-badge\u0026logo=docker\u0026logoColor=white\" alt=\"Docker Badge\"\u003e\n\t\u003c/p\u003e\n\u003c/div\u003e\n\n---\n\n## Table of Contents\n- [Overview](#overview)\n- [Features](#features)\n- [Tech Stack](#tech-stack)\n- [Project Structure](#project-structure)\n- [Setup](#setup)\n- [Docker](#docker)\n- [PDF Generation](#pdf-generation)\n- [Database Management](#database-management)\n- [PyTest](#pytest)\n- [Web Interface](#web-interface)\n- [SSH](#ssh)\n- [Misc](#misc)\n- [Customization](#customization)\n- [API Endpoints](#api-endpoints)\n- [License](#license)\n\n---\n\n## Overview  \n\n`loto-report-generation` is a full-stack Python application designed to automate the creation, management, and retrieval of LOTO reports. It features:\n- Custom PDF generation with templates, fonts, and images\n- Dynamic JSON/photo uploads for report creation\n- Web interface and API endpoints for interactive management\n- Role-based authentication and audit logging for security and compliance\n- Dockerized deployment for easy setup across environments\n\nThis makes it ideal for industrial safety documentation, operational reporting, and automated compliance workflows.\n\n---\n\n## Features  \n\n- **Secure Authentication \u0026 Roles**: Supports login, account creation, and owner/admin/user roles. Sensitive actions are restricted based on role.\n- **Audit Logging**: Tracks user actions like logins, report creation, uploads, deletions, and status changes with timestamps and IP addresses.\n- **Automated PDF Generation**: Generates professional LOTO reports using templates, fonts, and embedded images.\n- **JSON \u0026 Photo Uploads**: Accepts structured data and image files for flexible report creation.\n- **Web Interface**: Interactive frontend for managing users, reports, and logs with visual status indicators and responsive design.\n- **RESTful API Endpoints**: Full set of API routes for programmatic access to users, reports, files, and audit logs.\n- **Environment Config via `.env`**: Supports local and production deployment using environment variables.\n- **Modular \u0026 Extensible**: Components like auth, PDF generation, API, and database are self-contained and easily extendable.\n- **Docker \u0026 Nginx Integration**: Full containerized setup for consistent deployments.\n\n\n---\n\n## Tech Stack  \n\n- **Frontend**: HTML, CSS, JavaScript, TypeScript, Markdown\n- **Backend**: Python, FastAPI, Jinja2\n- **Database**: MongoDB with GridFS for file storage\n- **Authentication \u0026 Security**: JWT, role-based access\n- **Other Tools**: Docker, Nginx, UV, Pytest, ReportLab, GitHub Actions\n\n\n---\n\n## Project Structure  \n\n```\nloto-report-generator/\n├─ .env                               # Environment variables\n├─ .env.dev                           # Local/test environment variables\n├─ Dockerfile                         # Python image for the app\n├─ docker-compose.yml                 # Docker setup: FastAPI, MongoDB, Nginx\n├─ includes/                          # Images, fonts, and other static assets\n├─ logs/                              # Application and server logs\n├─ mongod.conf                        # MongoDB configuration\n├─ nginx.conf                         # Nginx reverse proxy config\n├─ src/\n│  ├── api/                           # Backend FastAPI endpoints and utilities\n│  │   ├── main.py                    # FastAPI entry point\n│  │   ├── auth_utils.py              # Authentication helpers\n│  │   └── logging_config.py          # Central logging config\n│  ├── database/                      # MongoDB helper scripts\n│  ├── pdf/                           # PDF generation scripts\n│  ├── tests/                         # Pytest test cases\n│  └── web/                           # Frontend web interface\n│      ├── main.html                  # Entry HTML page\n│      ├── static/                    # CSS, JS, and assets\n│      └── templates/                 # Jinja2 HTML templates\n└─ temp/                              # Temporary files\n```\n\n---\n\n## Setup\n\n1. **Install uv**\n\tDownload and install [uv](https://github.com/astral-sh/uv) from the official repository or use:\n```bash\ncurl -LsSf https://astral.sh/uv/install.sh | sh\nuv self update\nuv python install 3.13.2\n```\n\n2. **Create a virtual environment**\n```bash\nuv venv\nsource .venv/bin/activate\n# --- OR ---\nsource .venv/Scripts/activate\n```\n\n3. **Confirm virtual environment**\n```bash\n# --- MacOS ---\nwhere python\n# --- Windows ---\nwhich python\n```\n\n3. **Install dependencies**\n```bash\n# --- Might need to clear cache if it is an issue ---\nuv cache clean  # Optional\nuv sync\nuv pip install -e .\n```\n\n4. **Add a `.env` file**\n\t- Create a file named `.env` in the project root directory.\n\t- Add the following line (replace with your server IP, no quotes):\n```bash\nSERVER_IP = your.server.ip.address\n```\n- Should start with http:// (ex. SERVER_IP=http://127.0.0.1:8000)\n\n---\n\n### Docker\n\n1. Clone the repo\n```bash\ngit clone \u003cyour-repo-url\u003e\ncd loto-report-generator\n```\n\n2. Create the `.env` file\n```bash\nvi .env\n```\n- add the following\n```text\n# Mongo Credentials\nMONGO_USER=...\nMONGO_PASSWORD=...\nMONGO_HOST=...\nMONGO_PORT=...\nMONGO_DB=...\n\n# App Config\nAPP_ENV=dev\n\n# Server IP\nSERVER_IP=http://{SERVER_IP_ADDRESS}:8000\nTEST_SERVER_IP=http://localhost:8000\n\n# JWT\nSECRET_KEY=...\nADMIN_JWT=...\n\n# email\nSENDER_EMAIL=...\nSENDER_PASSWORD=...\nSENDGRID_API_KEY=...\n\nDEFAULT_EMAIL=...\n\n# Cleanup URL\nCLEANUP_URL=http://lotogenerator.app/cleanup_orphan_photos\n```\n\n3. Start Docker\n```bash\ndocker-compose up -d --build\n```\n- remove `-d` if you want to see everything behind the covers\n\n4. Test Connection\n```bash\ndocker logs -f fastapi_app\ndocker logs -f mongo_db\ncurl http://\u003cserver-ip\u003e/api/docs\n```\n- The first 2 should open logs for both `uvicron` and `mongo`\n- The last should return the FastAPI docs page if Nginx is configured correctly\n\n5. Run a test docker container locally\n```bash\ndocker compose -f docker-compose.yml -f docker-compose.dev.yml --env-file .env.dev up --build\n```\n\n---\n\n## PDF Generation\n\n1. Start server\n``` bash\nuvicorn src.api.main:app --reload --host 127.0.0.1 --port 8000\n```\n2. Add temp assets to the `temp/` folder for usage in the scripts\n3. Run the automate_pdf script:\n```python\npython automate_pdf.py $JSON_FILE\n```\n-- or --\n```python\npython generate_pdf.py $JSON_FILE\n```\n\n---\n\n## Database Management\n\n1. Configure and start MongoDB (Mac example)\n``` bash\n# one-time\nbrew tap mongodb/brew\nbrew install mongodb-community@7.0\n\n# run on login (recommended)\nbrew services start mongodb-community@7.0\n\n# verify it’s listening\nlsof -nP -iTCP:27017 -sTCP:LISTEN\n```\n\n2. Double check mongosh\n```bash\nmongosh \"mongodb://127.0.0.1:27017/?directConnection=true\" --eval \"db.adminCommand({ ping: 1 })\"\n```\n\n3. Look at the current database on the web\n``` txt\nhttp://localhost:8000/pdf_list\n```\n\n---\n\n### PyTest\n\n1. Install `poppler`\n``` bash\n# Windows\nhttps://github.com/oschwartz10612/poppler-windows/releases/\n```\n- Download latest ZIP release on GitHub\n- Extract the zip somewhere, e.g. `C:\\tools\\poppler-23.12.0\\`\n\t- or latest release number\n\t- if folder doesn't exist, add it\n- Add the `bin` folder to your`PATH` environment variable:\n\t- `C:\\tools\\poppler-23.12.0\\Library\\bin`\n\t- System variables\n\n``` bash\n# MacOS\nbrew install poppler\n```\n\n2. Check `poppler` installation\n``` bash\npdfinfo -v\npdftoppm -v\n```\n\n3. Double check `pre-commit` and `pre-push` hooks are updated\n``` bash\nuv add pre-commit # if not already added\npre-commit install --hook-type pre-commit --hook-type pre-push\npre-commit autoupdate\npre-commit run --all-files\n```\n\n4. Run the tests\n``` bash\npytest -v -s --no-summary src/tests/test_pdf_scripts.py\n```\n- `-q` is optional to reduce more of the unnecessary text in the test\n\n5. Check the results\n\u003cpre style=\"color:green;\"\u003e\n============= ___ passed in ___s =============\n\u003c/pre\u003e\n- You are looking out for all of them to say `PASSED`\n\n---\n\n### Server CRON jobs\n\n1. **Python Interpreter Path:** Find where your Python executable is located.\n```bash\nwhich python3\n```\n\n2. **Script Path:** Find the full path to your Python file\n```bash\npwd {SCRIPT_FILE}\n```\n\n3. **Open the Crontab Editor:** This command opens your user-specific cron schedule file.\n```bash\ncrontab -e\n```\n\n4. Set up task (server is in UTC time +5)\n```bash\n0 8 * * * {PYTHON_PATH} {SCRIPT_PATH} \u003e\u003e {LOG_PATH} 2\u003e\u00261\n```\n\n---\n\n## Web Interface\n\n1. Get TypeScript running\n```bash\nnpm install -D typescript\nnpx tsc --init\n```\n\n2. Compile TypeScript to JavaScript\n```bash\nnpx tsc src/web/scripts/input_form_3.ts --outDir src/web/scripts\n```\n\n---\n\n### SSH\n\n1. Start agent\n``` bash\neval \"$(ssh-agent -s -t 8h)\"\n```\n\n2. Add key to agent\n```bash\nssh-add ~/.ssh/id_ed25519\n```\n- Enter passphrase and it should stop bugging you\n\n3. Add this to the `.bashrc` or `.zshrc` file\n```bashrc\nSSH_ENV=\"$HOME/.ssh/agent-environment\"\n\nfunction start_agent {\n    echo \"Starting ssh-agent...\"\n    ssh-agent -s \u003e \"$SSH_ENV\"\n    source \"$SSH_ENV\" \u003e /dev/null\n    ssh-add -t 8h ~/.ssh/id_ed25519\n}\n\n# Source ssh-agent environment file if exists\nif [ -f \"$SSH_ENV\" ]; then\n    source \"$SSH_ENV\" \u003e /dev/null\n    # Check if agent is still running\n    ps -p $SSH_AGENT_PID \u003e /dev/null || {\n        start_agent;\n    }\nelse\n    start_agent;\nfi\n```\n\n---\n\n### Misc\n\n1. Count lines of code\n```bash\ngit ls-files src | xargs wc -l | sort -n\nfind . -type f -print0 | xargs -0 wc -l\n```\n\n2. Counts the number of times a file has been committed\n```bash\ngit log --diff-filter=AM --name-only --pretty=format: | grep -v '^$' | sort | uniq -c | sort -n\n```\n\n3. Git file endings\n```bash\ngit config --global core.autocrlf\n```\n\n--- \n\n## Customization\n\n- Upload `.json` file with your own data\n- Modify `generate_pdf.py` for custom report layouts\n- Place additional images or fonts in the `inlcudes/` folder as needed\n\n---\n\n## API Endpoints\n\n```\nhttp://localhost:8000/docs\n```\n- For more info on each endpoint\n\n| Endpoint                               | Methods   | Category          | Description                                                                         |\n|----------------------------------------|-----------|-------------------|-------------------------------------------------------------------------------------|\n| `/health`                              | GET       | 🩺 Health         | Simple public health-check endpoint returning a JSON status payload.                |\n| `/login`                               | GET, POST | 🔒 Auth Page      | Displays the login page and processes user authentication.                          |\n| `/logout`                              | POST      | 🔒 Auth API       | Logs out the current user by clearing the authentication cookie/token.              |\n| `/create_account`                      | GET, POST | 🔒 Auth Page      | Displays the create-account page and creates a new (inactive) user.                 |\n| `/check-username-email`                | GET       | 🔒 Auth API       | Checks whether a username or email is already in use during signup.                 |\n| `/forgot_password`                     | GET       | 🔒 Auth Page      | Displays the forgot-password page where users can request a backup code.            |\n| `/send_backup_code`                    | POST      | 🔒 Auth API       | Sends the current backup code to the specified email and rotates the code.          |\n| `/verify_backup_code`                  | POST      | 🔒 Auth API       | Verifies a submitted backup code for an email and rotates to a new code.            |\n| `/update-verification-attempts`        | POST      | ⚙️ Auth API       | Updates the verification-attempt counter for a given email address.                 |\n| `/reset_password`                      | POST      | 🔒 Auth API       | Resets a user’s password and sends a confirmation email.                            |\n| `/jwt_test`                            | GET       | ⚙️ Auth API       | Verifies the current JWT and returns a success JSON response.                       |\n| `/users`                               | GET       | 🖥️ Admin Page     | Displays the HTML user management page (owner only).                                |\n| `/users_json`                          | GET       | ⚙️ Admin API      | Returns JSON data for all users (owner only).                                       |\n| `/change_status`                       | POST      | ⚙️ Admin API      | Activates or deactivates a user account (owner only).                               |\n| `/update_role`                         | POST      | ⚙️ Admin API      | Updates a user’s role (owner only).                                                 |\n| `/delete_user`                         | POST      | ⚙️ Admin API      | Deletes a user account and sends a notification email (owner only).                 |\n| `/update-login-attempts`               | POST      | ⚙️ Admin API      | Updates the stored login-attempt counter for a specified user (JWT-protected).      |\n| `/audit_logs`                          | GET       | 🖥️ Admin Page     | Displays the audit log viewer page (owner only).                                    |\n| `/audit_logs_json`                     | GET       | ⚙️ Admin API      | Returns recent audit logs as JSON (owner only).                                     |\n| `/create_report`                       | GET       | 🖥️ Reports Page   | Displays the HTML form for creating a new report.                                   |\n| `/upload/`                             | POST      | ⚙️ Reports API    | Uploads JSON report data and photos to create or update a report in MongoDB/GridFS. |\n| `/pdf_list`                            | GET       | 🖥️ Reports Page   | Displays the HTML page listing all available reports.                               |\n| `/pdf_list_json`                       | GET       | ⚙️ Reports API    | Returns a paginated JSON list of reports and their metadata.                        |\n| `/view_report/{report_name}`           | GET       | 🖥️ Reports Page   | Displays detailed metadata and photos for a single report as an HTML page.          |\n| `/metadata/{report_name}`              | GET       | ⚙️ Reports API    | Returns stored metadata for a specific report (without JSON payload or photos).     |\n| `/download_report_files/{report_name}` | GET       | ⚙️ Reports API    | Returns JSON containing download URLs for a report’s JSON and photos.               |\n| `/download_json/{report_name}`         | GET       | ⚙️ Reports API    | Downloads the raw JSON data for the specified report.                               |\n| `/download_pdf/{report_name}`          | GET       | ⚙️ Reports API    | Downloads or streams the generated PDF file for the specified report.               |\n| `/download_photo/{photo_id}`           | GET       | ⚙️ Reports API    | Downloads an individual photo file from GridFS by its ID.                           |\n| `/photo/{photo_id}`                    | GET       | ⚙️ Reports API    | Returns a photo image from GridFS by its ID for inline display.                     |\n| `/remove_report/{report_name}`         | GET, POST | ⚙️ Reports API    | Deletes a report document from the database (shared photos are retained).           |\n| `/cleanup_orphan_photos`               | GET, POST | 🧹 Maintenance    | Deletes photos in GridFS that are not referenced by any report.                     |\n| `/clear/`                              | POST      | 🧹 Maintenance    | Clears all temporary files in the server’s temp directory.                          |\n| `/db_status`                           | GET       | 🧩 Maintenance    | Checks the database connection and returns a status message.                        |\n\n---\n\n## License\n\nThis project is licensed under the MIT License, which means you are free to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the software, as long as you include the original copyright and license notice in any copy of the software. The software is provided \"as is,\" without warranty of any kind.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkscardinal%2Floto-report-generator","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkscardinal%2Floto-report-generator","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkscardinal%2Floto-report-generator/lists"}