Initial commit

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
D Stephenson
2026-04-22 20:43:59 +00:00
commit 6c21525b79
23 changed files with 36533 additions and 0 deletions
+40
View File
@@ -0,0 +1,40 @@
# ── RV50x Template Manager — Docker ignore ──────────────────────────────────
# Files and folders that should NOT be copied into the Docker image.
# Data directories are mounted as volumes instead.
# Virtual environment — dependencies are installed fresh in the image
.venv/
# Data directories — these are bind-mounted as named volumes
template_downloads/
template_uploads/
xml_templates/
# Legacy and backup scripts — not needed in the container
download_csv.py
upload_csv.py
rv50x_template_manager.py
modems.csv
# Standalone CLI scripts — the web app (app.py) replaces these in Docker
download.py
upload.py
# Reports — live in the volumes, not the image
report_*.txt
# Python cache
__pycache__/
*.pyc
*.pyo
*.pyd
# Environment files — never bake secrets into the image
.env
.env.*
# Editor and OS files
.DS_Store
.vscode/
*.swp
*.swo
+51
View File
@@ -0,0 +1,51 @@
# ── RV50x Template Manager — Environment Configuration ─────────────────────
#
# Copy this file to .env and fill in your values.
# Never commit .env to version control — it contains secrets.
#
# docker-compose reads this file automatically.
# For Portainer: paste these into the Environment Variables section of the stack.
# ───────────────────────────────────────────────────────────────────────────
# ── NocoDB connection ────────────────────────────────────────────────────────
#
# If using the built-in NocoDB from this stack:
# NOCODB_URL=http://nocodb:8080
#
# If pointing at an existing external NocoDB instance:
# NOCODB_URL=http://192.168.16.130:8080
#
# After spinning up the stack and importing your data, update the IDs below
# by reading them from the NocoDB browser URL:
# http://host/w98wg3nt/{NOCODB_BASE_ID}/{NOCODB_TABLE_ID}/{NOCODB_VIEW_ID}/...
NOCODB_URL=http://nocodb:8080
NOCODB_TOKEN=your-nocodb-api-token-here
NOCODB_BASE_ID=your-base-id-here
NOCODB_TABLE_ID=your-table-id-here
NOCODB_VIEW_ID=your-view-id-here
# ── PostgreSQL password ──────────────────────────────────────────────────────
# Used internally by NocoDB. Choose a strong password.
# You won't need to type this anywhere — it's only used container-to-container.
POSTGRES_PASSWORD=changeme_use_a_strong_password_here
# ── NocoDB JWT secret ────────────────────────────────────────────────────────
# Used to sign NocoDB auth tokens. Any long random string works.
# Generate one with: openssl rand -hex 32
NC_JWT_SECRET=changeme_use_a_long_random_string_here
# ── Playwright timeouts (optional — defaults shown) ──────────────────────────
# Increase these if your modems are slow to respond.
# Values are in milliseconds.
# PAGE_TIMEOUT=90000
# DOWNLOAD_TIMEOUT=120000
# UPLOAD_TIMEOUT=120000
# MAX_RETRIES=3
+4
View File
@@ -0,0 +1,4 @@
.env
certs/
__pycache__/
*.pyc
+54
View File
@@ -0,0 +1,54 @@
# ── RV50x Template Manager ─────────────────────────────────────────────────
# Uses the official Playwright Python image which has Chromium and all
# required system libraries pre-installed — no manual apt installs needed.
# ───────────────────────────────────────────────────────────────────────────
FROM mcr.microsoft.com/playwright/python:v1.44.0-jammy
# Set working directory inside the container
WORKDIR /app
# ── Install Python dependencies ─────────────────────────────────────────────
# Copy requirements first so Docker caches this layer — only rebuilds when
# requirements.txt changes, not every time app code changes.
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# ── Install Playwright's Chromium browser ───────────────────────────────────
# The base image has the system libs; this installs the actual browser binary.
RUN playwright install chromium
# ── Copy application files ──────────────────────────────────────────────────
COPY app.py .
COPY index.html .
# ── Create data directories ─────────────────────────────────────────────────
# These will be overridden by volume mounts in docker-compose, but we create
# them here so the app works even if volumes aren't configured.
RUN mkdir -p /data/template_downloads \
/data/template_uploads \
/data/xml_templates
# ── Environment defaults ────────────────────────────────────────────────────
# These are overridden by the .env file or docker-compose environment section.
ENV DOWNLOAD_DIR=/data/template_downloads
ENV UPLOAD_DIR=/data/template_uploads
ENV TEMPLATES_DIR=/data/xml_templates
ENV PYTHONUNBUFFERED=1
# ── Expose port ─────────────────────────────────────────────────────────────
# Change the left number in docker-compose.yml to remap to a different host port.
EXPOSE 8000
# ── Start the application ───────────────────────────────────────────────────
# SSL_CERT and SSL_KEY env vars are optional — if set, uvicorn serves HTTPS.
# If not set, falls back to plain HTTP (useful for local dev).
CMD ["sh", "-c", "\
if [ -n \"$SSL_CERT\" ] && [ -n \"$SSL_KEY\" ]; then \
echo 'Starting with HTTPS'; \
python -m uvicorn app:app --host 0.0.0.0 --port 8000 \
--ssl-certfile \"$SSL_CERT\" --ssl-keyfile \"$SSL_KEY\"; \
else \
echo 'Starting with HTTP (no SSL vars set)'; \
python -m uvicorn app:app --host 0.0.0.0 --port 8000; \
fi"]
+730
View File
@@ -0,0 +1,730 @@
# RV50x Template Manager — Docker Edition
A containerized web-based tool for managing configuration templates on Sierra Wireless AirLink RV50x modems. Runs as a full Docker stack including the web application, NocoDB, and PostgreSQL — start and stop the entire thing with a single command.
---
## Table of Contents
1. [Stack Overview](#stack-overview)
2. [Requirements](#requirements)
3. [Project Structure](#project-structure)
4. [First-Time Setup](#first-time-setup)
5. [The .env File](#the-env-file)
6. [Building and Starting](#building-and-starting)
7. [NocoDB Setup](#nocodb-setup)
8. [Updating the App](#updating-the-app)
9. [Managing the Stack](#managing-the-stack)
10. [Using Portainer](#using-portainer)
11. [Migrating to a New Machine](#migrating-to-a-new-machine)
12. [Reconnecting to a New NocoDB Instance](#reconnecting-to-a-new-nocodb-instance)
13. [Data and Volumes](#data-and-volumes)
14. [Troubleshooting](#troubleshooting)
---
## Stack Overview
The stack runs three containers:
| Container | Image | Purpose | Default Port |
|---|---|---|---|
| `rv50x-manager` | Built from `Dockerfile` | FastAPI web app + Playwright | Your choice |
| `rv50x-nocodb` | `nocodb/nocodb:latest` | NocoDB UI and API | 8090 |
| `rv50x-postgres` | `postgres:16-alpine` | PostgreSQL database for NocoDB | Internal only |
All three containers communicate over a private internal Docker network. PostgreSQL is never exposed to the host — only NocoDB can reach it. The web app talks to NocoDB via the internal hostname `nocodb`.
```
Your Browser
rv50x-manager (YOUR_PORT) ──→ nocodb (8090) ──→ postgres (internal)
Your Modems (port 443, via Playwright)
```
---
## Requirements
- Docker Engine 24+ or Docker Desktop
- Docker Compose v2 (`docker compose` or `docker-compose`)
- Network access from the Docker host to your modems on port 443
- Portainer (optional, but recommended for easy management)
---
## Project Structure
```
/opt/rv50x-manager/
├── app.py ← FastAPI backend
├── index.html ← Web UI
├── requirements.txt ← Python dependencies
├── Dockerfile ← Container build instructions
├── docker-compose.yml ← Stack definition
├── .env ← Your secrets and config (never commit this)
├── .env.example ← Template for .env
└── .dockerignore ← Files excluded from the Docker image
```
**Not needed in the Docker folder** (kept separately if you want CLI access):
- `download.py` / `upload.py` — standalone CLI scripts
- `modems.csv` — legacy device list
- `.venv/` — Python virtual environment
---
## First-Time Setup
### Step 1 — Install Docker
**Fedora / RHEL / Rocky:**
```bash
sudo dnf install -y docker docker-compose-plugin
sudo systemctl start docker
sudo systemctl enable docker
sudo usermod -aG docker $USER # log out and back in after this
```
**Debian / Ubuntu:**
```bash
sudo apt-get install -y docker.io docker-compose-plugin
sudo systemctl start docker
sudo systemctl enable docker
sudo usermod -aG docker $USER
```
Verify Docker is working:
```bash
docker run hello-world
```
### Step 2 — Copy project files to the host machine
Create the install directory and set permissions, then copy your files:
```bash
# Create the directory and give your user ownership
sudo mkdir -p /opt/rv50x-manager
sudo chown $USER:$USER /opt/rv50x-manager
# Copy all project files into it
cp app.py index.html requirements.txt Dockerfile \
docker-compose.yml .env.example .dockerignore \
/opt/rv50x-manager/
cd /opt/rv50x-manager
```
### Step 3 — Create your .env file
```bash
cd /opt/rv50x-manager
cp .env.example .env
nano .env # or use any text editor
```
See [The .env File](#the-env-file) section below for what to put in each field.
### Step 4 — Set your port
Open `/opt/rv50x-manager/docker-compose.yml` and find this line under `rv50x-manager`:
```yaml
ports:
- "YOUR_PORT:8000"
```
Replace `YOUR_PORT` with the port number you want to use on the host machine. For example:
```yaml
ports:
- "8001:8000"
```
The app will then be accessible at `http://host-ip:8001`.
### Step 5 — Build and start
```bash
cd /opt/rv50x-manager
docker-compose up -d
```
This builds the `rv50x-manager` image and starts all three containers. The first build takes a few minutes because it downloads the Playwright base image and installs Chromium.
### Step 6 — Set up NocoDB
See [NocoDB Setup](#nocodb-setup) below.
### Step 7 — Update .env with NocoDB IDs
After importing your data into NocoDB, update `.env` with the real IDs and restart the manager:
```bash
docker-compose restart rv50x-manager
```
### Step 8 — Open the app
```
http://your-docker-host-ip:YOUR_PORT
```
---
## The .env File
Create this file by copying `.env.example` and filling in your values. It is read automatically by `docker-compose`. **Never commit this file to version control** — it contains passwords and API tokens.
```bash
# ── NocoDB connection ──────────────────────────────────────────────────────
#
# Use "http://nocodb:8080" to connect to the NocoDB container in this stack.
# Use your external NocoDB URL if you prefer to point at an existing instance.
NOCODB_URL=http://nocodb:8080
# Your NocoDB API token.
# Get it from: NocoDB → Profile (bottom-left) → Team & Settings → API Tokens
# Tokens look like: eWU_ilelaCtNy1JzC7vf41DokkqFOovcLHM0zVml
NOCODB_TOKEN=your-api-token-here
# These three IDs come from the NocoDB browser URL after you import your data.
# The URL structure is:
# http://host:8090/{org_id}/{BASE_ID}/{TABLE_ID}/{VIEW_ID}/table-name
# Leave as placeholder for now — update after NocoDB setup (Step 6 above).
NOCODB_BASE_ID=your-base-id-here
NOCODB_TABLE_ID=your-table-id-here
NOCODB_VIEW_ID=your-view-id-here
# ── PostgreSQL ─────────────────────────────────────────────────────────────
# Internal password used between NocoDB and PostgreSQL containers only.
# You will never need to type this manually — make it long and strong.
POSTGRES_PASSWORD=SomeLongStrongPassword123!
# ── NocoDB JWT secret ──────────────────────────────────────────────────────
# Any long random string used to sign NocoDB authentication tokens.
# Generate one with: openssl rand -hex 32
NC_JWT_SECRET=paste-a-long-random-string-here
# ── Playwright timeouts (optional) ────────────────────────────────────────
# Uncomment and adjust if your modems are slow to respond. Values in ms.
# PAGE_TIMEOUT=90000
# DOWNLOAD_TIMEOUT=120000
# UPLOAD_TIMEOUT=120000
# MAX_RETRIES=3
```
### Generating secrets
```bash
# Generate a strong JWT secret
openssl rand -hex 32
# Generate a strong password
openssl rand -base64 24
```
---
## Building and Starting
### Build the image
```bash
docker-compose build
```
Only needed when `app.py`, `index.html`, or `requirements.txt` change. Skipped automatically if you just change `.env`.
### Start the stack
```bash
docker-compose up -d
```
The `-d` flag runs containers in the background (detached mode). Without it the logs stream to your terminal and the stack stops when you close the terminal.
### Check that everything is running
```bash
docker-compose ps
```
You should see all three containers with status `running` or `healthy`.
### View logs
```bash
# All containers
docker-compose logs -f
# Just the web app
docker-compose logs -f rv50x-manager
# Just NocoDB
docker-compose logs -f rv50x-nocodb
```
### Stop the stack
```bash
docker-compose stop
```
Stops all containers. Data is preserved in volumes. Start again with `docker-compose up -d`.
### Remove containers (keep data)
```bash
docker-compose down
```
Removes containers but keeps all named volumes (your modem data, templates, downloads). Safe to run before a rebuild.
### Remove everything including data ⚠
```bash
docker-compose down -v
```
**This deletes all volumes** including your NocoDB database and all template files. Only use this if you want a completely clean slate.
---
## NocoDB Setup
After starting the stack for the first time, NocoDB needs to be configured before the web app can use it.
### Step 1 — Open NocoDB
```
http://your-docker-host-ip:8090
```
### Step 2 — Create your account
On first launch NocoDB will prompt you to create an admin account. Use a strong password and note the credentials.
### Step 3 — Create a new base
Click **+ New Base** and name it something like `Cell Modems`.
### Step 4 — Import your modem data from CSV
1. Export your current modem data from your existing NocoDB instance: **toolbar → Download → CSV**
2. In the new NocoDB, click **+ Add or import** → **Import from CSV**
3. Upload the CSV file
4. NocoDB will auto-detect all columns including `hostname`, `ip_address`, `dept`, `password`, etc.
5. Confirm the import
### Step 5 — Recreate filtered views
The CSV import brings the data but not the views. Recreate them manually:
**Electric view:**
1. In the left sidebar, click **+ Add View** → **Grid**
2. Name it `Electric`
3. Click **Filter****+ Add Filter**
4. Set: `dept` `is` `ELEC`
**Gas & Water view:**
1. Click **+ Add View** → **Grid**
2. Name it `Gas & Water`
3. Click **Filter****+ Add Filter**
4. Set: `dept` `is` `GW`
### Step 6 — Get the IDs from the browser URL
Navigate to your Cell Modems table. The URL will look like:
```
http://host:8090/abc123def/BASE_ID_HERE/TABLE_ID_HERE/VIEW_ID_HERE/cell-modems
```
Copy `BASE_ID_HERE`, `TABLE_ID_HERE`, and `VIEW_ID_HERE` (use the All view ID).
### Step 7 — Generate an API token
1. Click your profile avatar (bottom-left)
2. Go to **Team & Settings → API Tokens**
3. Click **Add Token**, give it a name like `rv50x-manager`
4. Copy the token
### Step 8 — Update .env and restart
```bash
cd /opt/rv50x-manager
nano .env
# Update NOCODB_TOKEN, NOCODB_BASE_ID, NOCODB_TABLE_ID, NOCODB_VIEW_ID
docker-compose restart rv50x-manager
```
### Step 9 — Verify the connection
Open the web app and check that the device groups show the correct counts. If devices appear, the connection is working.
---
## Updating the App
When `app.py` or `index.html` change:
```bash
# Stop the stack
docker-compose stop
# Rebuild the manager image (NocoDB and Postgres don't need rebuilding)
docker-compose build rv50x-manager
# Start everything back up
docker-compose up -d
```
When only `.env` changes (NocoDB IDs, token, timeouts):
```bash
docker-compose restart rv50x-manager
```
When `requirements.txt` changes (new Python packages):
```bash
docker-compose build rv50x-manager
docker-compose up -d
```
---
## Managing the Stack
### Start individual services
```bash
docker-compose up -d postgres # start just postgres
docker-compose up -d nocodb # start just nocodb
docker-compose up -d rv50x-manager # start just the web app
```
### Restart a single service
```bash
docker-compose restart rv50x-manager
```
### Rebuild and restart a single service
```bash
docker-compose up -d --build rv50x-manager
```
### Execute a command inside a running container
```bash
# Open a shell in the web app container
docker exec -it rv50x-manager bash
# Check Python packages installed
docker exec rv50x-manager pip list
# Test NocoDB connection from inside the container
docker exec rv50x-manager curl -s http://nocodb:8080/api/v1/health
```
### Access the PostgreSQL database directly
```bash
docker exec -it rv50x-postgres psql -U nocodb -d nocodb
```
---
## Using Portainer
Portainer gives you a browser-based UI to manage the entire stack without needing SSH.
### Install Portainer (if not already installed)
```bash
docker volume create portainer_data
docker run -d \
-p 9000:9000 \
--name portainer \
--restart=always \
-v /var/run/docker.sock:/var/run/docker.sock \
-v portainer_data:/data \
portainer/portainer-ce:latest
```
Then open `http://host-ip:9000` and create your admin account.
### Deploy the stack via Portainer
1. Go to **Stacks → + Add Stack**
2. Name it `rv50x`
3. Paste the contents of `docker-compose.yml` into the editor
4. Scroll down to **Environment Variables**
5. Click **+ Add an environment variable** for each line in your `.env` file:
| Name | Value |
|---|---|
| `NOCODB_URL` | `http://nocodb:8080` |
| `NOCODB_TOKEN` | your token |
| `NOCODB_BASE_ID` | your base ID |
| `NOCODB_TABLE_ID` | your table ID |
| `NOCODB_VIEW_ID` | your view ID |
| `POSTGRES_PASSWORD` | your password |
| `NC_JWT_SECRET` | your secret |
6. Click **Deploy the stack**
### Start and stop via Portainer
- **Stacks → rv50x → Start** — starts all containers
- **Stacks → rv50x → Stop** — stops all containers
- Individual containers: **Containers** list → click the start/stop icons
### Update the stack via Portainer
1. **Stacks → rv50x → Editor**
2. Paste updated `docker-compose.yml`
3. Click **Update the stack**
For code changes (`app.py`, `index.html`), you need to rebuild the image first from the command line:
```bash
docker-compose build rv50x-manager
```
Then update the stack in Portainer to pick up the new image.
---
## Migrating to a New Machine
### Step 1 — Export your NocoDB data
Before migrating, export your modem data as CSV from the current NocoDB instance so you can reimport it on the new machine.
In NocoDB: **Cell Modems table → toolbar → Download → CSV**
### Step 2 — Copy project files
From the old machine:
```bash
scp app.py index.html requirements.txt Dockerfile docker-compose.yml \
.env.example .dockerignore youruser@new-host:/tmp/rv50x-transfer/
```
**Do not copy `.env`** over an insecure connection — recreate it manually on the new machine.
On the new machine, move files into place:
```bash
sudo mkdir -p /opt/rv50x-manager
sudo chown $USER:$USER /opt/rv50x-manager
cp /tmp/rv50x-transfer/* /opt/rv50x-manager/
```
### Step 3 — Copy template files (optional)
If you want to keep your existing XML templates, downloaded configs, and upload files:
```bash
# Export volumes from the old machine
cd /opt/rv50x-manager
docker run --rm \
-v rv50x-manager_xml_templates:/data \
-v $(pwd):/backup \
alpine tar czf /backup/xml_templates.tar.gz -C /data .
docker run --rm \
-v rv50x-manager_template_downloads:/data \
-v $(pwd):/backup \
alpine tar czf /backup/template_downloads.tar.gz -C /data .
docker run --rm \
-v rv50x-manager_template_uploads:/data \
-v $(pwd):/backup \
alpine tar czf /backup/template_uploads.tar.gz -C /data .
# Copy archives to new machine
scp xml_templates.tar.gz template_downloads.tar.gz template_uploads.tar.gz \
youruser@new-host:/opt/rv50x-manager/
```
### Step 4 — Set up the new machine
```bash
# Install Docker (see First-Time Setup above)
# Files should already be in /opt/rv50x-manager from Step 2
cd /opt/rv50x-manager
# Create fresh .env with new passwords and secrets
cp .env.example .env
nano .env
# Set your port
nano docker-compose.yml
# Build and start
docker-compose up -d
```
### Step 5 — Restore template files (if copied)
```bash
cd /opt/rv50x-manager
# Restore xml_templates
docker run --rm \
-v rv50x-manager_xml_templates:/data \
-v $(pwd):/backup \
alpine tar xzf /backup/xml_templates.tar.gz -C /data
# Restore template_downloads
docker run --rm \
-v rv50x-manager_template_downloads:/data \
-v $(pwd):/backup \
alpine tar xzf /backup/template_downloads.tar.gz -C /data
# Restore template_uploads
docker run --rm \
-v rv50x-manager_template_uploads:/data \
-v $(pwd):/backup \
alpine tar xzf /backup/template_uploads.tar.gz -C /data
```
### Step 6 — Set up NocoDB on the new machine
Follow the [NocoDB Setup](#nocodb-setup) section — import your CSV, recreate views, get new IDs.
### Step 7 — Update .env with new NocoDB IDs
```bash
nano .env
docker-compose restart rv50x-manager
```
---
## Reconnecting to a New NocoDB Instance
If your NocoDB database is corrupted, rebuilt, or moved to a new server:
### Step 1 — Get the new connection details
1. Open the new NocoDB in your browser
2. Import your modem data from CSV
3. Recreate the Electric and Gas & Water filtered views
4. Go to **Profile → Team & Settings → API Tokens** → create a new token
5. Copy the base ID, table ID, and view ID from the browser URL
### Step 2 — Test the connection
```bash
curl -H "xc-token: YOUR_NEW_TOKEN" \
"http://new-nocodb-host:8090/api/v1/db/data/noco/BASE_ID/TABLE_ID?limit=1"
```
A successful response returns JSON with a `list` array containing your first modem row.
### Step 3 — Update .env
```bash
cd /opt/rv50x-manager
nano .env
```
Update these values:
```bash
NOCODB_URL=http://new-host:8090 # or http://nocodb:8080 if using the stack
NOCODB_TOKEN=your-new-token
NOCODB_BASE_ID=your-new-base-id
NOCODB_TABLE_ID=your-new-table-id
NOCODB_VIEW_ID=your-new-view-id
```
### Step 4 — Restart the manager
```bash
cd /opt/rv50x-manager
docker-compose restart rv50x-manager
```
Or in Portainer: **Stacks → rv50x → rv50x-manager → Restart**
---
## Data and Volumes
All persistent data lives in Docker named volumes. They survive `docker-compose down` and rebuilds, and are only deleted with `docker-compose down -v`.
| Volume | Contents | Maps to container path |
|---|---|---|
| `rv50x_template_manager_postgres_data` | NocoDB database | `/var/lib/postgresql/data` |
| `rv50x_template_manager_nocodb_data` | NocoDB config and uploads | `/usr/app/data` |
| `rv50x_template_manager_template_downloads` | Downloaded modem configs + reports | `/data/template_downloads` |
| `rv50x_template_manager_template_uploads` | Staged XML files for upload + reports | `/data/template_uploads` |
| `rv50x_template_manager_xml_templates` | Your XML builder templates | `/data/xml_templates` |
### List all volumes
```bash
docker volume ls | grep rv50x
```
### Back up a volume
```bash
# Backs up the xml_templates volume to a tar file in the current directory
docker run --rm \
-v rv50x_template_manager_xml_templates:/data \
-v $(pwd):/backup \
alpine tar czf /backup/xml_templates_backup.tar.gz -C /data .
```
### Back up the NocoDB database
```bash
cd /opt/rv50x-manager
docker exec rv50x-postgres \
pg_dump -U nocodb nocodb > nocodb_backup_$(date +%Y%m%d).sql
```
### Restore the NocoDB database
```bash
cd /opt/rv50x-manager
docker exec -i rv50x-postgres \
psql -U nocodb nocodb < nocodb_backup_20260413.sql
```
---
## Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| `docker-compose: command not found` | Using older Docker without compose plugin | Use `docker compose` (space not hyphen) or install `docker-compose-plugin` |
| Build fails with "no space left on device" | Docker image cache full | Run `docker system prune` to free space |
| `rv50x-manager` exits immediately after start | App crash on startup — bad .env values | Run `docker-compose logs rv50x-manager` to see the error |
| NocoDB shows "Service Unavailable" | Postgres not ready yet | Wait 30s and refresh — healthcheck retries handle this automatically |
| Web app shows 0 devices | NocoDB IDs wrong or token invalid | Test with curl (see Reconnecting section), update .env, restart manager |
| `ERR_BASE_NOT_FOUND` in curl test | Wrong NOCODB_BASE_ID | Re-read the ID from the NocoDB browser URL |
| `ERR_AUTHENTICATION_REQUIRED` | Wrong or expired token | Regenerate token in NocoDB → API Tokens |
| Modems show in All but not Electric/Gas & Water | Wrong dept field values | Check actual `dept` values in NocoDB — must be exactly `ELEC` and `GW` |
| Playwright / Chromium crashes | Missing system library | The Playwright base image should have everything — check `docker-compose logs rv50x-manager` |
| Template upload times out | Modem slow or unreachable | Increase `UPLOAD_TIMEOUT` in `.env`, restart manager |
| Can't reach NocoDB at port 8090 | Port conflict or firewall | Change the left port in `docker-compose.yml` under `nocodb:` ports |
| Can't reach web app | Port conflict or firewall | Change `YOUR_PORT` in `docker-compose.yml` |
| `permission denied` on docker commands | User not in docker group | Run `sudo usermod -aG docker $USER` then log out and back in |
| Volume data missing after `docker-compose down` | Used `down -v` by mistake | Data is gone — restore from backup or re-import CSV |
+1277
View File
File diff suppressed because it is too large Load Diff
+121
View File
@@ -0,0 +1,121 @@
# ── RV50x Template Manager — Full Stack ────────────────────────────────────
#
# Services:
# rv50x-manager FastAPI web app + Playwright
# nocodb NocoDB UI and API
# postgres PostgreSQL database for NocoDB
#
# Usage:
# Start: docker-compose up -d
# Stop: docker-compose stop
# Destroy: docker-compose down (data volumes preserved)
# Logs: docker-compose logs -f rv50x-manager
#
# Port mapping — change the LEFT number to use a different host port:
# rv50x-manager: http://host-ip:YOUR_PORT
# nocodb: http://host-ip:8090
# postgres: not exposed (internal only)
# ───────────────────────────────────────────────────────────────────────────
services:
# ── RV50x Template Manager ────────────────────────────────────────────────
rv50x-manager:
build:
context: .
dockerfile: Dockerfile
container_name: rv50x-manager
restart: "no" # manual start only
ports:
- "8002:8000" # ← change YOUR_PORT to your chosen port
volumes:
# Bind mounts — files are directly accessible on the host at these paths.
# No sudo needed, no docker volume commands needed.
- /opt/rv50x-manager/template_downloads:/data/template_downloads
- /opt/rv50x-manager/template_uploads:/data/template_uploads
- /opt/rv50x-manager/xml_templates:/data/xml_templates
- /opt/rv50x-manager/certs:/certs:ro # SSL certificates (read-only)
environment:
# ── NocoDB connection ──────────────────────────────────────────────
# To use the built-in NocoDB from this stack, set NOCODB_URL to:
# http://nocodb:8080
# To use an external NocoDB instance, set it to that URL instead.
NOCODB_URL: ${NOCODB_URL}
NOCODB_TOKEN: ${NOCODB_TOKEN}
NOCODB_BASE_ID: ${NOCODB_BASE_ID}
NOCODB_TABLE_ID: ${NOCODB_TABLE_ID}
NOCODB_VIEW_ID: ${NOCODB_VIEW_ID}
# ── SSL certificate paths (inside the container) ───────────────────
SSL_CERT: /certs/cert.pem
SSL_KEY: /certs/key.pem
# ── Tunable timeouts (optional) ────────────────────────────────────
PAGE_TIMEOUT: ${PAGE_TIMEOUT:-90000}
DOWNLOAD_TIMEOUT: ${DOWNLOAD_TIMEOUT:-120000}
UPLOAD_TIMEOUT: ${UPLOAD_TIMEOUT:-120000}
MAX_RETRIES: ${MAX_RETRIES:-3}
APP_USERNAME: ${APP_USERNAME}
APP_PASSWORD: ${APP_PASSWORD}
SESSION_SECRET: ${SESSION_SECRET}
SESSION_HOURS: ${SESSION_HOURS:-8}
depends_on:
nocodb:
condition: service_healthy
networks:
- rv50x-net
# ── NocoDB ───────────────────────────────────────────────────────────────
nocodb:
image: nocodb/nocodb:latest
container_name: rv50x-nocodb
restart: "no" # manual start only
ports:
- "8090:8080" # NocoDB UI on host port 8090
environment:
NC_DB: "pg://postgres:5432?u=nocodb&p=${POSTGRES_PASSWORD}&d=nocodb"
NC_AUTH_JWT_SECRET: ${NC_JWT_SECRET}
volumes:
- nocodb_data:/usr/app/data
depends_on:
postgres:
condition: service_healthy
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/api/v1/health"]
interval: 10s
timeout: 5s
retries: 10
start_period: 30s
networks:
- rv50x-net
# ── PostgreSQL ───────────────────────────────────────────────────────────
postgres:
image: postgres:16-alpine
container_name: rv50x-postgres
restart: "no" # manual start only
environment:
POSTGRES_USER: nocodb
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: nocodb
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U nocodb"]
interval: 5s
timeout: 3s
retries: 10
networks:
- rv50x-net
# Postgres is intentionally NOT exposed on a host port.
# Only NocoDB can reach it via the internal network.
# ── Named volumes — only postgres and nocodb use named volumes ───────────────
# template_downloads, template_uploads, and xml_templates use bind mounts
# above so files are directly accessible in /opt/rv50x-manager/ on the host.
volumes:
postgres_data:
nocodb_data:
# ── Internal network ────────────────────────────────────────────────────────
networks:
rv50x-net:
driver: bridge
+31
View File
@@ -0,0 +1,31 @@
# This is the internal Docker service name — leave as-is if using the
# built-in NocoDB from the stack. Change to your external NocoDB IP
# if you want to point at your existing instance instead.
NOCODB_URL=http://nocodb:8080
# Your NocoDB API token — get this from:
# NocoDB → Profile (bottom left) → Team & Settings → API Tokens
NOCODB_TOKEN=eWU_ilelaCtNy1JzC7vf41DokkqFOovcLHM0zVml
# These three come from the NocoDB browser URL after you import your data.
# The URL looks like: http://host:8090/{org}/{BASE_ID}/{TABLE_ID}/{VIEW_ID}/...
NOCODB_BASE_ID=pdq96x915xt4a0m
NOCODB_TABLE_ID=mkewnr53ahqvnt9
NOCODB_VIEW_ID=vwl7qvxo1xclvawz
# Make up a strong password — you'll never type this manually,
# it's only used between the NocoDB and Postgres containers internally.
POSTGRES_PASSWORD=SomeLongStrongPassword123!
# Any long random string — used to sign NocoDB login tokens.
# Generate one with: openssl rand -hex 32
NC_JWT_SECRET=a1b2c3d4e5f6...
The order of operations for first setup:
Create .env with the passwords and secrets filled in — but leave the NocoDB IDs as placeholder values for now
Run docker-compose up -d to start the stack
Open NocoDB at http://host-ip:8090, create your account, import your CSV, recreate your views
Copy the real base/table/view IDs from the browser URL into .env
Run docker-compose restart rv50x-manager to pick up the new IDs
App is ready at http://host-ip:YOUR_PORT
+1672
View File
File diff suppressed because it is too large Load Diff
+8
View File
@@ -0,0 +1,8 @@
fastapi
uvicorn[standard]
playwright
pandas
openpyxl
paramiko
itsdangerous
python-multipart
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff