Style check fixes:
- Replace unicode box-drawing characters with ASCII (|-- instead of ├──)
- Replace emoji status markers with [OK]/[FAIL]/[WARN] tags
- Replace arrows (→ with ->, ↔ with <->) and em-dashes (— with --)
- Add venv-quick to excluded directories in check script
- Exclude darkwing report files (findings.sarif, report.md)
Backup finalization fix:
- Fix finalize-restore endpoint to not claim the upgrade sentinel
- Endpoint now checks for pending upgrade and returns {queued: true}
- Leaves UPGRADE_SENTINEL for background watcher to claim and process
- Prevents inline Alembic runs that can outlast request timeout
Claude Code context:
- Add .claude/ directory with INDEX.md and QUICK-REFERENCE.md
- Add CLAUDE.md with project overview and conventions
- Helps Claude Code understand project structure and rules
Fixes failing test: test_the_endpoint_does_not_run_the_upgrade_itself
Fixes naming/style CI check violations
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
363 lines
10 KiB
Markdown
363 lines
10 KiB
Markdown
# ShopDB Local Development Setup (Windows)
|
|
|
|
Complete guide for setting up ShopDB Flask development environment on Windows.
|
|
|
|
## Prerequisites
|
|
|
|
- Python 3.13+
|
|
- Docker Desktop (for MySQL and testing)
|
|
- Git
|
|
- Node.js 20+ (for frontend development)
|
|
|
|
## Quick Start
|
|
|
|
```bash
|
|
# 1. Clone and enter repo
|
|
cd C:/Users/YOUR_USERNAME/Documents/github/flask-shopdb
|
|
|
|
# 2. Create Python virtual environment
|
|
python -m venv venv
|
|
./venv/Scripts/activate
|
|
pip install -r requirements.txt
|
|
|
|
# 3. Start MySQL in Docker
|
|
docker compose up -d db
|
|
|
|
# 4. Configure environment
|
|
cp .env.docker .env
|
|
# Edit .env and set DATABASE_URL (see below)
|
|
|
|
# 5. Install and enable plugins
|
|
export DATABASE_URL="mysql+pymysql://root:root-dev-password-not-for-production@localhost:3307/shopdb_flask_dev"
|
|
./venv/Scripts/flask.exe plugin install backups computers employees endpoints geenforce guides knowledgebase machines measuringtools network notifications printedparts printers slides tools usb warranty
|
|
./venv/Scripts/flask.exe plugin enable <each-plugin-name>
|
|
|
|
# 6. Start Flask
|
|
./venv/Scripts/python.exe run_dev.py
|
|
```
|
|
|
|
Visit http://localhost:5001 and login with `admin` / `admin`.
|
|
|
|
## Detailed Setup
|
|
|
|
### 1. Database Setup
|
|
|
|
MySQL runs in Docker on **port 3307** (not 3306) to avoid conflicts with any local MySQL instance.
|
|
|
|
```bash
|
|
# Start MySQL container
|
|
docker compose up -d db
|
|
|
|
# Verify it's running
|
|
docker ps --filter name=shopdb-dev-db
|
|
# Should show: 127.0.0.1:3307->3306/tcp
|
|
|
|
# Test connection
|
|
docker exec shopdb-dev-db mysql -u root -p'root-dev-password-not-for-production' -e "SELECT 1;"
|
|
```
|
|
|
|
### 2. Environment Configuration
|
|
|
|
Create `.env` file with:
|
|
|
|
```bash
|
|
# Database (port 3307 because Docker maps it there)
|
|
DATABASE_URL=mysql+pymysql://root:root-dev-password-not-for-production@localhost:3307/shopdb_flask_dev
|
|
|
|
# Flask
|
|
FLASK_ENV=development
|
|
FLASK_DEBUG=1
|
|
|
|
# Secrets (dev only)
|
|
SECRET_KEY=docker-dev-secret-key-change-for-production
|
|
JWT_SECRET_KEY=docker-dev-jwt-secret-change-for-production
|
|
|
|
# CORS
|
|
CORS_ORIGINS=http://localhost:5173,http://localhost:5001
|
|
|
|
# API Port
|
|
API_PORT=5001
|
|
|
|
# ShopDB Label
|
|
SHOPDB_ENV_LABEL=LOCAL-DEV
|
|
|
|
# Log Level
|
|
LOG_LEVEL=DEBUG
|
|
|
|
# Backup Storage (for local dev)
|
|
SHOPDB_BACKUPS_ROOT=C:/Users/YOUR_USERNAME/Documents/github/flask-shopdb/backups
|
|
```
|
|
|
|
**Important:** Change `YOUR_USERNAME` to your actual Windows username.
|
|
|
|
### 3. Import Database (Optional)
|
|
|
|
If you have a production database dump:
|
|
|
|
```bash
|
|
# Import latest.sql
|
|
docker exec -i shopdb-dev-db mysql -u root -p'root-dev-password-not-for-production' shopdb_flask_dev < latest.sql
|
|
|
|
# Run migrations
|
|
export DATABASE_URL="mysql+pymysql://root:root-dev-password-not-for-production@localhost:3307/shopdb_flask_dev"
|
|
./venv/Scripts/flask.exe db upgrade
|
|
```
|
|
|
|
### 4. Plugin Installation
|
|
|
|
All 17 plugins must be installed before they can be enabled:
|
|
|
|
```bash
|
|
# Set DATABASE_URL for CLI commands
|
|
export DATABASE_URL="mysql+pymysql://root:root-dev-password-not-for-production@localhost:3307/shopdb_flask_dev"
|
|
|
|
# Install all plugins
|
|
for plugin in backups computers employees endpoints geenforce guides knowledgebase machines measuringtools network notifications printedparts printers slides tools usb warranty; do
|
|
./venv/Scripts/flask.exe plugin install $plugin
|
|
done
|
|
|
|
# Enable all plugins
|
|
for plugin in backups computers employees endpoints geenforce guides knowledgebase machines measuringtools network notifications printedparts printers slides tools usb warranty; do
|
|
./venv/Scripts/flask.exe plugin enable $plugin
|
|
done
|
|
```
|
|
|
|
**Why this is necessary:**
|
|
- Plugin state is tracked in `instance/plugins.json`, not the database
|
|
- When you import a prod database, it has plugin tables but no plugin registry
|
|
- Installing registers plugins in the JSON file
|
|
- Enabling makes them active (but requires app restart)
|
|
|
|
### 5. Reset Admin Password
|
|
|
|
If you imported a prod database and don't know the password:
|
|
|
|
```bash
|
|
# Generate new password hash
|
|
./venv/Scripts/python.exe -c "from werkzeug.security import generate_password_hash; print(generate_password_hash('admin'))"
|
|
|
|
# Reset via MySQL
|
|
docker exec shopdb-dev-db mysql -u root -p'root-dev-password-not-for-production' shopdb_flask_dev -e "
|
|
UPDATE users
|
|
SET passwordhash = 'PASTE_HASH_HERE',
|
|
failedlogins = 0,
|
|
lockeduntil = NULL
|
|
WHERE username = 'admin';
|
|
"
|
|
```
|
|
|
|
Or use the simpler method:
|
|
```bash
|
|
docker exec shopdb-dev-db mysql -u root -p'root-dev-password-not-for-production' shopdb_flask_dev -e "
|
|
UPDATE users
|
|
SET passwordhash = 'scrypt:32768:8:1\$B4LhpPrM6qxijInK\$b99b47a0bb5fd45761a1823f401f70a3ceccf71a5d38dc433378aa07426ef8eaf7e679ff3dd7b0ce9772600f83368ac642e9419b9beaa49f87483318bae34547',
|
|
failedlogins = 0,
|
|
lockeduntil = NULL
|
|
WHERE username = 'admin';
|
|
"
|
|
```
|
|
|
|
Password is now: `admin` / `admin`
|
|
|
|
### 6. Starting Flask
|
|
|
|
**Always use `run_dev.py`** (not `flask run` directly):
|
|
|
|
```bash
|
|
./venv/Scripts/python.exe run_dev.py
|
|
```
|
|
|
|
This script:
|
|
- Loads `.env` file with DATABASE_URL
|
|
- Sets all environment variables
|
|
- Starts Flask without auto-reloader (prevents env var loss)
|
|
- Binds to 0.0.0.0:5001
|
|
|
|
**Do NOT use:**
|
|
- `flask run` - Won't load `.env` properly
|
|
- `flask run --reload` - Loses environment variables
|
|
|
|
### 7. Frontend Development (Optional)
|
|
|
|
To develop the Vue frontend:
|
|
|
|
```bash
|
|
cd frontend
|
|
npm ci
|
|
npm run dev
|
|
```
|
|
|
|
Frontend dev server runs on http://localhost:5173 and proxies API requests to Flask on port 5001.
|
|
|
|
For production build (served by Flask):
|
|
```bash
|
|
cd frontend
|
|
npm run build
|
|
# Outputs to frontend/dist/
|
|
```
|
|
|
|
## Troubleshooting
|
|
|
|
### "Can't connect to MySQL on 127.0.0.1:3306"
|
|
|
|
**Cause:** Flask is trying port 3306 instead of 3307.
|
|
|
|
**Fix:**
|
|
1. Make sure `.env` has `DATABASE_URL=...@localhost:3307/...`
|
|
2. Use `run_dev.py` to start Flask (NOT `flask run`)
|
|
3. If problem persists, restart Flask completely
|
|
|
|
### "Cannot import name '_backups_configured'"
|
|
|
|
**Cause:** Merge conflict between wj and wilm backup implementations.
|
|
|
|
**Fix:** This was fixed in commit `8a1fe0ada8` - pull latest wj branch.
|
|
|
|
### "Plugin X is not installed"
|
|
|
|
**Cause:** Plugin registry (`instance/plugins.json`) doesn't exist or is incomplete.
|
|
|
|
**Fix:** Run plugin installation commands (section 4 above).
|
|
|
|
### "Access is denied: '/backups'"
|
|
|
|
**Cause:** Backup system trying to use Linux path on Windows.
|
|
|
|
**Fix:**
|
|
1. Add to `.env`: `SHOPDB_BACKUPS_ROOT=C:/Users/YOUR_USERNAME/Documents/github/flask-shopdb/backups`
|
|
2. Create directory: `mkdir backups`
|
|
3. Restart Flask with `run_dev.py`
|
|
|
|
### Docker npm install fails (Zscaler proxy)
|
|
|
|
**Cause:** Corporate proxy intercepts HTTPS, npm doesn't trust certificate.
|
|
|
|
**Fix:** Extract Zscaler root CA certificate:
|
|
|
|
```powershell
|
|
# Run this PowerShell script
|
|
$request = [System.Net.HttpWebRequest]::Create('https://registry.npmjs.org/')
|
|
$request.Method = 'HEAD'
|
|
$request.Timeout = 10000
|
|
$response = $request.GetResponse()
|
|
$root = [System.Security.Cryptography.X509Certificates.X509Certificate2]::new($request.ServicePoint.Certificate)
|
|
$builder = [System.Text.StringBuilder]::new()
|
|
[void]$builder.AppendLine('-----BEGIN CERTIFICATE-----')
|
|
[void]$builder.AppendLine([System.Convert]::ToBase64String($root.RawData, 'InsertLineBreaks'))
|
|
[void]$builder.AppendLine('-----END CERTIFICATE-----')
|
|
Set-Content -Path 'C:\Users\270015376\Documents\github\flask-shopdb\corp-root-ca.pem' -Value $builder.ToString() -Encoding ASCII
|
|
```
|
|
|
|
Then rebuild Docker: `docker compose build --no-cache`
|
|
|
|
## Common Tasks
|
|
|
|
### Run Tests
|
|
```bash
|
|
pytest
|
|
pytest tests/test_specific.py
|
|
pytest -v -s # Verbose with print output
|
|
```
|
|
|
|
### Run Migrations
|
|
```bash
|
|
export DATABASE_URL="mysql+pymysql://root:root-dev-password-not-for-production@localhost:3307/shopdb_flask_dev"
|
|
./venv/Scripts/flask.exe db upgrade
|
|
```
|
|
|
|
### Create Migration
|
|
```bash
|
|
./venv/Scripts/flask.exe db migrate -m "Description of changes"
|
|
```
|
|
|
|
### Check Plugin Status
|
|
```bash
|
|
./venv/Scripts/flask.exe plugin list
|
|
```
|
|
|
|
### Enable/Disable Plugin
|
|
```bash
|
|
./venv/Scripts/flask.exe plugin enable slides
|
|
./venv/Scripts/flask.exe plugin disable slides
|
|
```
|
|
|
|
### Trigger Backup
|
|
```bash
|
|
# Via API (requires admin JWT token)
|
|
curl -X POST http://localhost:5001/api/shopdbbackups \
|
|
-H "Authorization: Bearer YOUR_TOKEN"
|
|
|
|
# Or via Docker
|
|
docker compose -f docker-compose.backup.yml up backup
|
|
```
|
|
|
|
## Architecture Notes
|
|
|
|
### Why Port 3307?
|
|
Docker MySQL maps internal port 3306 to host port 3307 to avoid conflicts with any local MySQL installation. This is configured in `docker-compose.yml`:
|
|
```yaml
|
|
ports:
|
|
- "127.0.0.1:3307:3306"
|
|
```
|
|
|
|
### Why run_dev.py?
|
|
Flask's `flask run` command uses an auto-reloader that spawns a subprocess. This subprocess loses environment variables loaded from `.env`. The `run_dev.py` wrapper:
|
|
1. Loads `.env` via python-dotenv BEFORE importing Flask
|
|
2. Uses `--no-reload` flag to prevent subprocess spawning
|
|
3. Ensures DATABASE_URL and other vars persist
|
|
|
|
### Plugin Architecture
|
|
- Plugins are Python packages in `plugins/` directory
|
|
- Plugin state tracked in `instance/plugins.json` (not database)
|
|
- Plugin tables tracked via Alembic migration chains
|
|
- Each plugin has own migrations in `plugins/<name>/migrations/`
|
|
|
|
### Backup System
|
|
- Designed for Docker compose deployment
|
|
- API queues backups via sentinel file (`.backup-requested`)
|
|
- Actual backup execution requires Docker sidecar container
|
|
- On Windows dev, API can queue but won't execute without Docker
|
|
|
|
## File Structure
|
|
|
|
```
|
|
flask-shopdb/
|
|
|-- .env # Local environment config (gitignored)
|
|
|-- run_dev.py # Local development launcher
|
|
|-- shopdb/ # Flask application
|
|
| |-- __init__.py # App factory
|
|
| |-- config.py # Configuration classes
|
|
| |-- core/ # Core models and APIs
|
|
| +-- plugins/ # Plugin manager
|
|
|-- plugins/ # Plugin packages
|
|
| |-- backups/
|
|
| |-- computers/
|
|
| +-- ...
|
|
|-- frontend/ # Vue 3 SPA
|
|
| |-- src/
|
|
| +-- dist/ # Production build (gitignored)
|
|
|-- instance/ # Runtime state (gitignored)
|
|
| |-- plugins.json # Plugin registry
|
|
| +-- ...
|
|
|-- migrations/ # Core Alembic migrations
|
|
|-- scripts/ # Deployment and backup scripts
|
|
|-- backups/ # Local backup storage (gitignored)
|
|
+-- tests/ # Test suite
|
|
```
|
|
|
|
## Git Workflow
|
|
|
|
- `main` branch - Published/public version (GitHub)
|
|
- `wj` branch - Production deployment (West Jefferson site)
|
|
- `wilm` branch - Development branch (upstream from benjamin.baker)
|
|
- `dev` branch - Ops deployment branch
|
|
|
|
**Never commit to `main` directly** - it's published to public wiki.
|
|
|
|
## Additional Resources
|
|
|
|
- [Backup Setup](scripts/backup/README.md)
|
|
- [Plugin Development](docs/PLUGIN-HOOKS.md)
|
|
- [ADRs](wiki/ADRs/)
|
|
- [API Docs](http://localhost:5001/api/docs) (when Flask is running)
|