fix: replace unicode characters and fix finalize-restore endpoint
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>
This commit is contained in:
116
.claude/INDEX.md
Normal file
116
.claude/INDEX.md
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
# ShopDB Index - Load Only What You Need
|
||||||
|
|
||||||
|
Quick pointers to avoid reading entire wiki or codebase.
|
||||||
|
|
||||||
|
## When Working On...
|
||||||
|
|
||||||
|
**Plugin development**:
|
||||||
|
- Read: `CLAUDE.md` (overview)
|
||||||
|
- Grep: `class BasePlugin` in `shopdb/api/__init__.py`
|
||||||
|
- Reference: `../shopdb-flask.wiki/ADR-001-asset-as-platform-contract.md`
|
||||||
|
|
||||||
|
**Database migrations**:
|
||||||
|
- Read: `../shopdb-flask.wiki/ADR-008-plugin-migration-ownership.md`
|
||||||
|
- Grep: `PLUGIN_TABLE_OWNERS` for dependency order
|
||||||
|
- Pattern: `plugins/{name}/migrations/versions/*.py`
|
||||||
|
|
||||||
|
**Site-specific config**:
|
||||||
|
- Read: `../shopdb-flask.wiki/ADR-015-site-specific-configuration.md`
|
||||||
|
- Locations: `shopdb/core/settings/`, `shopdb/core/seed/`, `scripts/site_imports/`
|
||||||
|
|
||||||
|
**Kiosk/device auth**:
|
||||||
|
- Read: `../shopdb-flask.wiki/ADR-016-credential-delivery.md`
|
||||||
|
- Grep: `requires_device_token`
|
||||||
|
|
||||||
|
**Deployment/branches**:
|
||||||
|
- Read: `../shopdb-flask.wiki/ADR-004-deployment-topology.md`
|
||||||
|
- Scripts: `~/Documents/github/*.cmd`
|
||||||
|
|
||||||
|
**Frontend plugin panels**:
|
||||||
|
- Read: `../shopdb-flask.wiki/ADR-010-frontend-plugin-hooks.md`
|
||||||
|
- Pattern: `plugins/{name}/frontend/views/*.vue`
|
||||||
|
|
||||||
|
**Testing**:
|
||||||
|
- Read: `tests/conftest.py` for fixtures
|
||||||
|
- Pattern: `tests/test_plugins/test_{plugin}_{feature}.py`
|
||||||
|
|
||||||
|
## Plugin Map (20 plugins)
|
||||||
|
|
||||||
|
Core asset types:
|
||||||
|
- `computers`, `endpoints`, `printers`, `machines`, `measuringtools`, `network`, `tools`
|
||||||
|
|
||||||
|
Workflows:
|
||||||
|
- `printedparts`, `usb`, `slides`, `notifications`
|
||||||
|
|
||||||
|
Integrations:
|
||||||
|
- `warranty`, `geenforce`, `guides`, `knowledgebase`
|
||||||
|
|
||||||
|
Admin:
|
||||||
|
- `employees`, `backups`
|
||||||
|
|
||||||
|
## Key Files
|
||||||
|
|
||||||
|
- `shopdb/api/__init__.py` - Stable import surface (75 lines)
|
||||||
|
- `shopdb/config.py` - App configuration
|
||||||
|
- `shopdb/core/models/asset.py` - Base asset model
|
||||||
|
- `docker-compose.yml` - Dev stack definition
|
||||||
|
- `scripts/check-naming-and-style.sh` - Pre-commit hook
|
||||||
|
|
||||||
|
## Git Branches
|
||||||
|
|
||||||
|
**Current repo state**:
|
||||||
|
- `wj` (checked out) - Prod branch for WJ shop floor
|
||||||
|
- `main` - Public GitHub mirror, deploys nowhere
|
||||||
|
- `origin/wilm` - Upstream changes from benjamin.baker (86 commits ahead)
|
||||||
|
- `origin/dev-be` - Historical dev branch
|
||||||
|
|
||||||
|
**Branch workflow** (ADR-004):
|
||||||
|
- `wj` -> prod deployment (WJ site)
|
||||||
|
- `dev` -> ops deployment (monitoring/pilot sites, on gitea only)
|
||||||
|
- `main` -> GitHub public mirror only (no deployments)
|
||||||
|
- `wilm` -> fetch from GitHub, review, merge to wj or dev
|
||||||
|
|
||||||
|
**On this work PC**:
|
||||||
|
- Has GitHub credentials (ge-aero/shopdb-flask)
|
||||||
|
- Fetch wilm: `~/Documents/github/fetch-wilm-branch.cmd`
|
||||||
|
- Bundle to dev PC: `~/Documents/github/pull-shopdb-bundle.cmd`
|
||||||
|
|
||||||
|
## Common Paths
|
||||||
|
|
||||||
|
- Plugin code: `plugins/{name}/`
|
||||||
|
- Core models: `shopdb/core/models/`
|
||||||
|
- Core API: `shopdb/core/api/`
|
||||||
|
- Frontend core: `frontend/src/`
|
||||||
|
- Tests: `tests/test_plugins/`
|
||||||
|
- Migrations: `migrations/` (core) + `plugins/{name}/migrations/`
|
||||||
|
- Scripts: `scripts/`
|
||||||
|
- Docs: `../shopdb-flask.wiki/`
|
||||||
|
|
||||||
|
## Avoid Reading These Unless Needed
|
||||||
|
|
||||||
|
- `docs/openapi.json` (489 lines, generated)
|
||||||
|
- `frontend/dist/` (build output)
|
||||||
|
- `node_modules/`, `venv/`, `.pytest_cache/`
|
||||||
|
- `agent/` (C# endpoint agent, 10k+ lines)
|
||||||
|
- Full wiki (68 files) - use index above
|
||||||
|
|
||||||
|
## Token-Efficient Patterns
|
||||||
|
|
||||||
|
1. **Grep before Read**: Find the exact file/function first
|
||||||
|
2. **Read with limit**: Use offset/limit for large files
|
||||||
|
3. **Reference by path**: Cite `file:line` instead of quoting code
|
||||||
|
4. **Use memory**: Store repeated patterns in memory files
|
||||||
|
5. **Wiki on-demand**: Only read specific ADRs when relevant
|
||||||
|
|
||||||
|
## One-Line ADR Summaries
|
||||||
|
|
||||||
|
- ADR-001: Asset is the platform primitive for all equipment
|
||||||
|
- ADR-004: Three air-gapped environments, bundle workflow
|
||||||
|
- ADR-006: Endpoint agent sends telemetry, receives commands
|
||||||
|
- ADR-007: Semantic versioning, GitHub releases
|
||||||
|
- ADR-008: Each plugin owns its migration chain, no conflicts
|
||||||
|
- ADR-009: Frontend plugins gate on backend features
|
||||||
|
- ADR-010: Plugins register Vue components via manifest
|
||||||
|
- ADR-013: Lean builds include only needed plugins
|
||||||
|
- ADR-015: Site literals go in settings/seed/site_imports only
|
||||||
|
- ADR-016: Kiosks use device tokens, not user sessions
|
||||||
152
.claude/QUICK-REFERENCE.md
Normal file
152
.claude/QUICK-REFERENCE.md
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
# ShopDB Quick Reference
|
||||||
|
|
||||||
|
Fast lookup for architectural decisions and contracts.
|
||||||
|
|
||||||
|
## Critical ADRs (in wiki)
|
||||||
|
|
||||||
|
- **ADR-001**: Asset as platform contract - Core asset model, BaseAsset, composition
|
||||||
|
- **ADR-004**: Deployment topology - Air-gapped sites, bundle workflow
|
||||||
|
- **ADR-006**: Collector contract - Endpoint agent protocol
|
||||||
|
- **ADR-007**: Product versioning - Semantic versioning, release process
|
||||||
|
- **ADR-008**: Plugin migration ownership - PLUGIN_TABLE_OWNERS, migration chains
|
||||||
|
- **ADR-013**: Plugin catalog - Lean builds, plugin manifest
|
||||||
|
- **ADR-015**: Site-specific configuration - Where site literals belong
|
||||||
|
- **ADR-016**: Credential delivery - Device tokens, kiosk auth
|
||||||
|
|
||||||
|
## Import Surface (stable API)
|
||||||
|
|
||||||
|
```python
|
||||||
|
from shopdb.api import (
|
||||||
|
# Database
|
||||||
|
db,
|
||||||
|
|
||||||
|
# Core Models
|
||||||
|
Asset, Employee, User, Building, MapLevel,
|
||||||
|
|
||||||
|
# Plugin Base
|
||||||
|
BasePlugin,
|
||||||
|
|
||||||
|
# Auth/Authz
|
||||||
|
requires_role, requires_device_token,
|
||||||
|
|
||||||
|
# Utilities
|
||||||
|
badges, datevalues, queryargs
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Plugin Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
plugins/{name}/
|
||||||
|
|-- plugin.py # BasePlugin subclass, manifest
|
||||||
|
|-- models/ # SQLAlchemy models
|
||||||
|
| +-- {name}.py
|
||||||
|
|-- api/ # Flask routes
|
||||||
|
| +-- routes.py
|
||||||
|
|-- frontend/ # Vue components
|
||||||
|
| +-- views/
|
||||||
|
| |-- {Name}List.vue
|
||||||
|
| |-- {Name}Detail.vue
|
||||||
|
| +-- {Name}Form.vue
|
||||||
|
|-- migrations/ # Alembic migrations
|
||||||
|
| +-- versions/
|
||||||
|
+-- services/ # Business logic (optional)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Database Naming
|
||||||
|
|
||||||
|
- **Tables**: `printedpart`, `mappeddrives` (lowercase concatenated)
|
||||||
|
- **Columns**: `serial_number`, `last_seen` (snake_case)
|
||||||
|
- **Enforced**: `scripts/check-naming-and-style.sh`
|
||||||
|
|
||||||
|
## Branch Model
|
||||||
|
|
||||||
|
- `wj` -> prod (WJ shop floor)
|
||||||
|
- `dev` -> ops (monitoring/pilot)
|
||||||
|
- `main` -> public mirror (GitHub only)
|
||||||
|
- `wilm` -> upstream (benjamin.baker's changes)
|
||||||
|
|
||||||
|
## Common Commands
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Run tests
|
||||||
|
pytest -v
|
||||||
|
|
||||||
|
# Check style
|
||||||
|
scripts/check-naming-and-style.sh
|
||||||
|
|
||||||
|
# Create migration
|
||||||
|
cd plugins/{name}
|
||||||
|
alembic revision -m "description"
|
||||||
|
|
||||||
|
# Dev server
|
||||||
|
docker compose up
|
||||||
|
|
||||||
|
# Bundle for dev PC
|
||||||
|
# (on work PC) ~/Documents/github/pull-shopdb-bundle.cmd
|
||||||
|
|
||||||
|
# Fetch wilm changes
|
||||||
|
# (on work PC) ~/Documents/github/fetch-wilm-branch.cmd
|
||||||
|
```
|
||||||
|
|
||||||
|
## Test Patterns
|
||||||
|
|
||||||
|
```python
|
||||||
|
def test_feature(client, db_session):
|
||||||
|
# Setup
|
||||||
|
item = Model(name="test")
|
||||||
|
db_session.add(item)
|
||||||
|
db_session.commit()
|
||||||
|
|
||||||
|
# Execute
|
||||||
|
resp = client.get('/api/plugin/endpoint')
|
||||||
|
|
||||||
|
# Assert
|
||||||
|
assert resp.status_code == 200
|
||||||
|
assert resp.json['name'] == 'test'
|
||||||
|
```
|
||||||
|
|
||||||
|
## Site Data Rules (ADR-015)
|
||||||
|
|
||||||
|
[OK] **Allowed**:
|
||||||
|
- `shopdb/core/settings/buildings.py`
|
||||||
|
- `shopdb/core/seed/*.sql`
|
||||||
|
- `scripts/site_imports/*.py`
|
||||||
|
|
||||||
|
[FAIL] **Forbidden**:
|
||||||
|
- Hardcoded in plugin code
|
||||||
|
- Embedded in migrations
|
||||||
|
- Configuration files
|
||||||
|
|
||||||
|
## Migration Ownership (ADR-008)
|
||||||
|
|
||||||
|
Each plugin owns its chain. Coordinate cross-plugin FK changes via PLUGIN_TABLE_OWNERS:
|
||||||
|
|
||||||
|
```python
|
||||||
|
PLUGIN_TABLE_OWNERS = {
|
||||||
|
'asset': 'core',
|
||||||
|
'employee': 'employees',
|
||||||
|
'pc': 'computers',
|
||||||
|
'endpoint': 'endpoints',
|
||||||
|
# ...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Device Tokens (ADR-016)
|
||||||
|
|
||||||
|
Kiosks require device authentication:
|
||||||
|
|
||||||
|
```python
|
||||||
|
from shopdb.api import requires_device_token
|
||||||
|
|
||||||
|
@bp.route('/kiosk/endpoint')
|
||||||
|
@requires_device_token(['usb_kiosk', 'parts_kiosk'])
|
||||||
|
def kiosk_view():
|
||||||
|
return render_template('kiosk.html')
|
||||||
|
```
|
||||||
|
|
||||||
|
## Wiki Location
|
||||||
|
|
||||||
|
Full docs: `~/Documents/github/shopdb-flask.wiki/`
|
||||||
|
|
||||||
|
All ADRs, deployment guides, plugin development docs live there.
|
||||||
27
.claude/settings.json
Normal file
27
.claude/settings.json
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"permissions": {
|
||||||
|
"allow": [
|
||||||
|
"bash:pytest*",
|
||||||
|
"bash:git status",
|
||||||
|
"bash:git log*",
|
||||||
|
"bash:git diff*",
|
||||||
|
"bash:git branch*",
|
||||||
|
"bash:docker compose*",
|
||||||
|
"bash:docker ps",
|
||||||
|
"bash:alembic*",
|
||||||
|
"bash:python scripts/*",
|
||||||
|
"grep:**",
|
||||||
|
"glob:**"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"hooks": {
|
||||||
|
"beforeCommit": [
|
||||||
|
"scripts/check-naming-and-style.sh"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"context": {
|
||||||
|
"alwaysInclude": [
|
||||||
|
".claude/INDEX.md"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
129
CLAUDE.md
Normal file
129
CLAUDE.md
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
# ShopDB Flask - Claude Code Context
|
||||||
|
|
||||||
|
ShopDB is a manufacturing floor asset management system built with Flask, Vue 3, and PostgreSQL, deployed across air-gapped GE Aerospace sites.
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
- **Backend**: Flask 3.x with SQLAlchemy 2.x, plugin-based architecture
|
||||||
|
- **Frontend**: Vue 3 with Vite, component-based UI with plugin panels
|
||||||
|
- **Database**: PostgreSQL 15+ with Alembic migrations per plugin
|
||||||
|
- **Deployment**: Docker Compose with gunicorn, air-gapped sites via bundle workflow
|
||||||
|
- **Plugins**: 20+ plugins (computers, endpoints, printers, machines, etc.)
|
||||||
|
|
||||||
|
## Critical Constraints
|
||||||
|
|
||||||
|
### ADR-015: Site-Specific Configuration
|
||||||
|
Site literals (building names, printer models, machine types) MUST live in:
|
||||||
|
- `shopdb/core/settings/` - Site settings (buildings, roles, etc.)
|
||||||
|
- `shopdb/core/seed/` - Initial data SQL
|
||||||
|
- `scripts/site_imports/` - Import tools for Maximo/WINC data
|
||||||
|
|
||||||
|
NEVER hardcode site-specific data in plugin code or migrations.
|
||||||
|
|
||||||
|
### ADR-008: Plugin Migration Ownership
|
||||||
|
Each plugin owns its migration chain in `plugins/{name}/migrations/versions/`.
|
||||||
|
- Migrations run in dependency order defined in PLUGIN_TABLE_OWNERS
|
||||||
|
- Each plugin's chain must be linear (no branches)
|
||||||
|
- Migrations run automatically on deploy
|
||||||
|
|
||||||
|
### Database Naming Convention
|
||||||
|
- **Tables**: lowercase concatenated, no underscores (e.g., `printedpart`, `mappeddrives`)
|
||||||
|
- **Columns**: snake_case (e.g., `serial_number`, `last_seen`)
|
||||||
|
- **Indexes/constraints**: follow PostgreSQL conventions
|
||||||
|
- Enforced by `scripts/check-naming-and-style.sh` pre-commit hook
|
||||||
|
|
||||||
|
### Branch and Deployment Model
|
||||||
|
- `wj` -> prod (shop floor at WJ site)
|
||||||
|
- `dev` -> ops (monitoring/pilot sites)
|
||||||
|
- `main` -> nothing (GitHub public mirror only)
|
||||||
|
- Three air gaps: dev PC <-> work PC <-> GitHub
|
||||||
|
- Bundles carry code across gaps (pull-shopdb-bundle.cmd, fetch-wilm-branch.cmd)
|
||||||
|
|
||||||
|
### Code Style
|
||||||
|
- Plain ASCII only - no unicode, emojis, or em-dashes
|
||||||
|
- No LLM attribution comments (no "Co-Authored-By: Claude")
|
||||||
|
- Minimal comments - code should be self-documenting
|
||||||
|
- No banned shorthand (e.g., "config" not "cfg", "initialize" not "init")
|
||||||
|
|
||||||
|
## Plugin Contract
|
||||||
|
|
||||||
|
Plugins extend ShopDB via:
|
||||||
|
- `plugin.py` - BasePlugin subclass with manifest, routes, hooks
|
||||||
|
- `models/` - SQLAlchemy models
|
||||||
|
- `api/` - Flask blueprints and routes
|
||||||
|
- `frontend/views/` - Vue components auto-discovered by router
|
||||||
|
- `migrations/versions/` - Alembic migrations
|
||||||
|
|
||||||
|
Import from `shopdb.api` for stable surfaces:
|
||||||
|
```python
|
||||||
|
from shopdb.api import db, Asset, Employee, BasePlugin
|
||||||
|
```
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
Full documentation in the GitHub wiki at `../shopdb-flask.wiki/`:
|
||||||
|
|
||||||
|
**Critical ADRs**:
|
||||||
|
- ADR-001: Asset as platform contract
|
||||||
|
- ADR-004: Deployment topology (air-gapped sites)
|
||||||
|
- ADR-006: Collector contract (endpoint agent)
|
||||||
|
- ADR-007: Product versioning and releases
|
||||||
|
- ADR-013: Plugin catalog and lean builds
|
||||||
|
- ADR-016: Credential delivery
|
||||||
|
|
||||||
|
**References**:
|
||||||
|
- `API-REFERENCE.md` - Core API surface
|
||||||
|
- `PLUGIN-DEVELOPMENT.md` - Plugin authoring guide
|
||||||
|
- `DEPLOYMENT.md` - Deploy runbooks per site
|
||||||
|
- `TESTING.md` - Test patterns and CI
|
||||||
|
|
||||||
|
## Development Workflow
|
||||||
|
|
||||||
|
1. Feature branches off `dev` or `wj`
|
||||||
|
2. **Run tests**: `pytest -v` (REQUIRED before claiming completion)
|
||||||
|
3. **Check naming/style**: `scripts/check-naming-and-style.sh` (REQUIRED before commit)
|
||||||
|
4. Bundle for other PC or push to GitHub
|
||||||
|
5. PR to `wj` (for prod) or `dev` (for ops)
|
||||||
|
|
||||||
|
## Boundaries & Constraints
|
||||||
|
|
||||||
|
**DO NOT**:
|
||||||
|
- Edit generated files (frontend/dist/, docs/openapi.json)
|
||||||
|
- Change dependencies (requirements.txt, package.json) without approval
|
||||||
|
- Commit code with unicode/emojis (plain ASCII only)
|
||||||
|
- Hardcode site-specific data (buildings, models, etc.) - use settings/seed
|
||||||
|
- Skip tests or pre-commit hooks
|
||||||
|
- Break plugin migration chains (keep linear, coordinate via PLUGIN_TABLE_OWNERS)
|
||||||
|
|
||||||
|
**ALWAYS**:
|
||||||
|
- Run `pytest -v` before claiming work complete
|
||||||
|
- Run `scripts/check-naming-and-style.sh` before committing
|
||||||
|
- Use `shopdb.api` imports, not internal paths
|
||||||
|
- Keep database table names lowercase concatenated
|
||||||
|
- Reference ADRs by number when making architectural decisions
|
||||||
|
- Verify bundle/deploy scripts work before pushing to branches
|
||||||
|
|
||||||
|
## Testing Patterns
|
||||||
|
|
||||||
|
- Feature tests in `tests/test_plugins/test_{plugin}_{feature}.py`
|
||||||
|
- Use fixtures from `conftest.py` (app, client, db_session)
|
||||||
|
- Test migrations with `test_plugin_migrations.py`
|
||||||
|
- Frontend specs alongside components: `*.spec.js`
|
||||||
|
|
||||||
|
## Common Pitfalls
|
||||||
|
|
||||||
|
- **Site data in code**: Use settings/seed, not hardcoded values
|
||||||
|
- **Migration conflicts**: Keep plugin chains linear, coordinate with PLUGIN_TABLE_OWNERS
|
||||||
|
- **Unicode**: ASCII only - check logs, comments, commit messages
|
||||||
|
- **Import surface**: Use `shopdb.api`, not internal paths
|
||||||
|
- **Device tokens**: Kiosks require device_token authentication (ADR-016)
|
||||||
|
|
||||||
|
## Work PC Role
|
||||||
|
|
||||||
|
This machine (work PC):
|
||||||
|
- Has GitHub credentials (ge-aero/shopdb-flask)
|
||||||
|
- Carries bundles between dev PC and GitHub
|
||||||
|
- Deploys to prod and ops servers
|
||||||
|
- Mirrors wilm branch from GitHub to gitea
|
||||||
|
|
||||||
|
For active development, use the dev PC with gitea.
|
||||||
@@ -53,13 +53,13 @@ cd C:\shopdb
|
|||||||
|
|
||||||
## What the Script Does
|
## What the Script Does
|
||||||
|
|
||||||
1. ✅ Checks Docker Desktop is running
|
1. [OK] Checks Docker Desktop is running
|
||||||
2. ✅ Creates .env from template
|
2. [OK] Creates .env from template
|
||||||
3. ✅ Builds frontend (if needed)
|
3. [OK] Builds frontend (if needed)
|
||||||
4. ✅ Builds Docker images (2-3 minutes, offline packages)
|
4. [OK] Builds Docker images (2-3 minutes, offline packages)
|
||||||
5. ✅ Starts all services (db + api + backup)
|
5. [OK] Starts all services (db + api + backup)
|
||||||
6. ✅ Runs database migrations
|
6. [OK] Runs database migrations
|
||||||
7. ✅ Sets admin password
|
7. [OK] Sets admin password
|
||||||
|
|
||||||
## Manual Deployment (Alternative)
|
## Manual Deployment (Alternative)
|
||||||
|
|
||||||
@@ -140,7 +140,7 @@ New-NetFirewallRule -DisplayName "ShopDB" -Direction Inbound -LocalPort 5001 -Pr
|
|||||||
|
|
||||||
**SSL certificate warnings:**
|
**SSL certificate warnings:**
|
||||||
- Normal for self-signed certs
|
- Normal for self-signed certs
|
||||||
- Click "Advanced" → "Proceed to localhost (unsafe)"
|
- Click "Advanced" -> "Proceed to localhost (unsafe)"
|
||||||
|
|
||||||
## Stop/Start
|
## Stop/Start
|
||||||
|
|
||||||
@@ -169,9 +169,9 @@ docker compose -f docker-compose.prebuilt.yml -f docker-compose.backup.yml up -d
|
|||||||
## Offline Deployment Notes
|
## Offline Deployment Notes
|
||||||
|
|
||||||
**This deployment works completely offline** because:
|
**This deployment works completely offline** because:
|
||||||
- ✅ Frontend pre-built and checked into repo
|
- [OK] Frontend pre-built and checked into repo
|
||||||
- ✅ Restic binary pre-downloaded (8.6MB in repo)
|
- [OK] Restic binary pre-downloaded (8.6MB in repo)
|
||||||
- ✅ No npm/pip downloads during Docker build
|
- [OK] No npm/pip downloads during Docker build
|
||||||
- ✅ All dependencies baked into images
|
- [OK] All dependencies baked into images
|
||||||
|
|
||||||
Perfect for air-gapped or restricted network environments!
|
Perfect for air-gapped or restricted network environments!
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
**Date:** 2026-09-07
|
**Date:** 2026-09-07
|
||||||
**Branch:** wj (post-wilm merge)
|
**Branch:** wj (post-wilm merge)
|
||||||
**Status:** ✅ Core system operational, backup watcher pending
|
**Status:** [OK] Core system operational, backup watcher pending
|
||||||
|
|
||||||
## What's Running
|
## What's Running
|
||||||
|
|
||||||
@@ -145,7 +145,7 @@ docker exec flask-shopdb-api-1 flask plugin disable PLUGIN_NAME
|
|||||||
|
|
||||||
### Backup Watcher Service
|
### Backup Watcher Service
|
||||||
|
|
||||||
**Status:** ❌ Build failed
|
**Status:** [FAIL] Build failed
|
||||||
**Reason:** Oracle Linux base image can't download packages due to SSL cert issues with Zscaler
|
**Reason:** Oracle Linux base image can't download packages due to SSL cert issues with Zscaler
|
||||||
**Impact:** Can't auto-execute backups on schedule (manual backups still work via one-shot container)
|
**Impact:** Can't auto-execute backups on schedule (manual backups still work via one-shot container)
|
||||||
|
|
||||||
@@ -166,9 +166,9 @@ Cannot download repomd.xml: Curl error (60): SSL peer certificate or SSH remote
|
|||||||
|
|
||||||
## Production Readiness
|
## Production Readiness
|
||||||
|
|
||||||
**Core Application:** ✅ Production ready
|
**Core Application:** [OK] Production ready
|
||||||
**Deployment Process:** ⚠️ Requires npm on host + Docker build
|
**Deployment Process:** [WARN] Requires npm on host + Docker build
|
||||||
**Backup System:** ❌ Not complete (watcher service build fails)
|
**Backup System:** [FAIL] Not complete (watcher service build fails)
|
||||||
|
|
||||||
### Recommended Next Steps
|
### Recommended Next Steps
|
||||||
|
|
||||||
@@ -191,9 +191,9 @@ Cannot download repomd.xml: Curl error (60): SSL peer certificate or SSH remote
|
|||||||
|
|
||||||
| Approach | Result | Time | Issue |
|
| Approach | Result | Time | Issue |
|
||||||
|----------|--------|------|-------|
|
|----------|--------|------|-------|
|
||||||
| Multi-stage Dockerfile | ❌ Failed | 15+ min | npm fails with Zscaler |
|
| Multi-stage Dockerfile | [FAIL] Failed | 15+ min | npm fails with Zscaler |
|
||||||
| Pre-built frontend | ✅ Success | 3 min | Works! |
|
| Pre-built frontend | [OK] Success | 3 min | Works! |
|
||||||
| Backup containers | ❌ Failed | N/A | Oracle Linux SSL issues |
|
| Backup containers | [FAIL] Failed | N/A | Oracle Linux SSL issues |
|
||||||
|
|
||||||
## Verification
|
## Verification
|
||||||
|
|
||||||
@@ -223,12 +223,12 @@ docker exec flask-shopdb-api-1 flask plugin list | grep Enabled | wc -l
|
|||||||
## Summary
|
## Summary
|
||||||
|
|
||||||
ShopDB Docker deployment is **operational** for core functionality:
|
ShopDB Docker deployment is **operational** for core functionality:
|
||||||
- ✅ Database running
|
- [OK] Database running
|
||||||
- ✅ API serving requests
|
- [OK] API serving requests
|
||||||
- ✅ Frontend built and accessible
|
- [OK] Frontend built and accessible
|
||||||
- ✅ Plugins installed and working
|
- [OK] Plugins installed and working
|
||||||
- ✅ HTTPS configured
|
- [OK] HTTPS configured
|
||||||
- ⚠️ Backup watcher needs work (but manual backups functional)
|
- [WARN] Backup watcher needs work (but manual backups functional)
|
||||||
|
|
||||||
The pre-built frontend approach successfully bypasses Zscaler corporate proxy issues. Build time is 2-3 minutes (vs 15+ for failed multi-stage approach).
|
The pre-built frontend approach successfully bypasses Zscaler corporate proxy issues. Build time is 2-3 minutes (vs 15+ for failed multi-stage approach).
|
||||||
|
|
||||||
|
|||||||
@@ -83,9 +83,9 @@ https://localhost:5001
|
|||||||
|
|
||||||
| Approach | Build Time | Corporate Proxy | Complexity | Production Ready |
|
| Approach | Build Time | Corporate Proxy | Complexity | Production Ready |
|
||||||
|----------|-----------|----------------|------------|------------------|
|
|----------|-----------|----------------|------------|------------------|
|
||||||
| Multi-stage Dockerfile | 15+ min | ❌ Fails | High | ❌ No |
|
| Multi-stage Dockerfile | 15+ min | [FAIL] Fails | High | [FAIL] No |
|
||||||
| Pre-built frontend | 2-3 min | ✅ Works | Medium | ⚠️ Yes (with CI/CD) |
|
| Pre-built frontend | 2-3 min | [OK] Works | Medium | [WARN] Yes (with CI/CD) |
|
||||||
| Pre-built images from registry | 30 sec | ✅ Works | Low | ✅ Yes |
|
| Pre-built images from registry | 30 sec | [OK] Works | Low | [OK] Yes |
|
||||||
|
|
||||||
## Next Steps
|
## Next Steps
|
||||||
|
|
||||||
|
|||||||
@@ -369,19 +369,19 @@ The `instance_data` volume persists:
|
|||||||
|
|
||||||
```
|
```
|
||||||
flask-shopdb/
|
flask-shopdb/
|
||||||
├── docker-compose.yml # Main stack definition
|
|-- docker-compose.yml # Main stack definition
|
||||||
├── docker-compose.backup.yml # Backup service
|
|-- docker-compose.backup.yml # Backup service
|
||||||
├── Dockerfile # Multi-stage build
|
|-- Dockerfile # Multi-stage build
|
||||||
├── .env # Environment config (gitignored)
|
|-- .env # Environment config (gitignored)
|
||||||
├── corp-root-ca.pem # Zscaler cert (gitignored)
|
|-- corp-root-ca.pem # Zscaler cert (gitignored)
|
||||||
├── certs/ # TLS certificates (gitignored)
|
|-- certs/ # TLS certificates (gitignored)
|
||||||
│ ├── server.crt
|
| |-- server.crt
|
||||||
│ └── server.key
|
| +-- server.key
|
||||||
├── scripts/
|
|-- scripts/
|
||||||
│ ├── docker-entrypoint.sh # Container startup
|
| |-- docker-entrypoint.sh # Container startup
|
||||||
│ └── bootstrap-plugins.sh # Plugin auto-install
|
| +-- bootstrap-plugins.sh # Plugin auto-install
|
||||||
└── deploy/
|
+-- deploy/
|
||||||
└── site-profile-universal.json # Full plugin list
|
+-- site-profile-universal.json # Full plugin list
|
||||||
```
|
```
|
||||||
|
|
||||||
## Next Steps
|
## Next Steps
|
||||||
|
|||||||
@@ -322,27 +322,27 @@ Flask's `flask run` command uses an auto-reloader that spawns a subprocess. This
|
|||||||
|
|
||||||
```
|
```
|
||||||
flask-shopdb/
|
flask-shopdb/
|
||||||
├── .env # Local environment config (gitignored)
|
|-- .env # Local environment config (gitignored)
|
||||||
├── run_dev.py # Local development launcher
|
|-- run_dev.py # Local development launcher
|
||||||
├── shopdb/ # Flask application
|
|-- shopdb/ # Flask application
|
||||||
│ ├── __init__.py # App factory
|
| |-- __init__.py # App factory
|
||||||
│ ├── config.py # Configuration classes
|
| |-- config.py # Configuration classes
|
||||||
│ ├── core/ # Core models and APIs
|
| |-- core/ # Core models and APIs
|
||||||
│ └── plugins/ # Plugin manager
|
| +-- plugins/ # Plugin manager
|
||||||
├── plugins/ # Plugin packages
|
|-- plugins/ # Plugin packages
|
||||||
│ ├── backups/
|
| |-- backups/
|
||||||
│ ├── computers/
|
| |-- computers/
|
||||||
│ └── ...
|
| +-- ...
|
||||||
├── frontend/ # Vue 3 SPA
|
|-- frontend/ # Vue 3 SPA
|
||||||
│ ├── src/
|
| |-- src/
|
||||||
│ └── dist/ # Production build (gitignored)
|
| +-- dist/ # Production build (gitignored)
|
||||||
├── instance/ # Runtime state (gitignored)
|
|-- instance/ # Runtime state (gitignored)
|
||||||
│ ├── plugins.json # Plugin registry
|
| |-- plugins.json # Plugin registry
|
||||||
│ └── ...
|
| +-- ...
|
||||||
├── migrations/ # Core Alembic migrations
|
|-- migrations/ # Core Alembic migrations
|
||||||
├── scripts/ # Deployment and backup scripts
|
|-- scripts/ # Deployment and backup scripts
|
||||||
├── backups/ # Local backup storage (gitignored)
|
|-- backups/ # Local backup storage (gitignored)
|
||||||
└── tests/ # Test suite
|
+-- tests/ # Test suite
|
||||||
```
|
```
|
||||||
|
|
||||||
## Git Workflow
|
## Git Workflow
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
## Executive Summary
|
## Executive Summary
|
||||||
|
|
||||||
🔴 **NOT PRODUCTION READY** in current state without significant operational investment.
|
[CRITICAL] **NOT PRODUCTION READY** in current state without significant operational investment.
|
||||||
|
|
||||||
The Docker deployment requires extensive manual setup, multiple services, and assumes expertise with Docker Compose, restic backup systems, and Flask plugin architecture. This assessment documents current gaps and provides recommendations.
|
The Docker deployment requires extensive manual setup, multiple services, and assumes expertise with Docker Compose, restic backup systems, and Flask plugin architecture. This assessment documents current gaps and provides recommendations.
|
||||||
|
|
||||||
@@ -13,22 +13,22 @@ The Docker deployment requires extensive manual setup, multiple services, and as
|
|||||||
|
|
||||||
### What Works
|
### What Works
|
||||||
|
|
||||||
✅ **Flask application code** - Stable, tested, 17 plugins functional
|
[OK] **Flask application code** - Stable, tested, 17 plugins functional
|
||||||
✅ **Database schema** - Migrations work, data model is sound
|
[OK] **Database schema** - Migrations work, data model is sound
|
||||||
✅ **Frontend build** - Vue 3 SPA compiles and serves correctly
|
[OK] **Frontend build** - Vue 3 SPA compiles and serves correctly
|
||||||
✅ **Docker images** - Build successfully (with caveats below)
|
[OK] **Docker images** - Build successfully (with caveats below)
|
||||||
✅ **Plugin architecture** - Clean, documented, extensible
|
[OK] **Plugin architecture** - Clean, documented, extensible
|
||||||
|
|
||||||
### Critical Gaps
|
### Critical Gaps
|
||||||
|
|
||||||
❌ **One-command deployment** - Requires 7-10 manual steps
|
[FAIL] **One-command deployment** - Requires 7-10 manual steps
|
||||||
❌ **Corporate proxy handling** - Zscaler cert extraction needed for npm
|
[FAIL] **Corporate proxy handling** - Zscaler cert extraction needed for npm
|
||||||
❌ **Backup system complexity** - 3 containers + restic + scheduler
|
[FAIL] **Backup system complexity** - 3 containers + restic + scheduler
|
||||||
❌ **TLS certificate generation** - Manual OpenSSL commands required
|
[FAIL] **TLS certificate generation** - Manual OpenSSL commands required
|
||||||
❌ **Plugin initialization** - Must run `apply-profile` after first start
|
[FAIL] **Plugin initialization** - Must run `apply-profile` after first start
|
||||||
❌ **Password reset** - No default admin password, requires manual SQL
|
[FAIL] **Password reset** - No default admin password, requires manual SQL
|
||||||
❌ **Build time** - 15+ minutes with corporate proxy
|
[FAIL] **Build time** - 15+ minutes with corporate proxy
|
||||||
❌ **Documentation scattered** - 3 different setup guides (Docker, Local, Backup)
|
[FAIL] **Documentation scattered** - 3 different setup guides (Docker, Local, Backup)
|
||||||
|
|
||||||
## Why Is This So Complex?
|
## Why Is This So Complex?
|
||||||
|
|
||||||
@@ -86,7 +86,7 @@ The Docker deployment requires extensive manual setup, multiple services, and as
|
|||||||
- Manual updates
|
- Manual updates
|
||||||
- No containerization benefits
|
- No containerization benefits
|
||||||
|
|
||||||
**Production Ready:** ⚠️ **YES** (with installer improvements)
|
**Production Ready:** [WARN] **YES** (with installer improvements)
|
||||||
|
|
||||||
### Option 2: Docker Pre-Built Images (RECOMMENDED)
|
### Option 2: Docker Pre-Built Images (RECOMMENDED)
|
||||||
|
|
||||||
@@ -110,7 +110,7 @@ The Docker deployment requires extensive manual setup, multiple services, and as
|
|||||||
- Need image registry
|
- Need image registry
|
||||||
- Initial registry setup
|
- Initial registry setup
|
||||||
|
|
||||||
**Production Ready:** ✅ **YES** (requires CI/CD setup)
|
**Production Ready:** [OK] **YES** (requires CI/CD setup)
|
||||||
|
|
||||||
### Option 3: Docker Source Build (CURRENT)
|
### Option 3: Docker Source Build (CURRENT)
|
||||||
|
|
||||||
@@ -141,7 +141,7 @@ The Docker deployment requires extensive manual setup, multiple services, and as
|
|||||||
- Many failure points
|
- Many failure points
|
||||||
- Requires Docker expertise
|
- Requires Docker expertise
|
||||||
|
|
||||||
**Production Ready:** 🔴 **NO** (too complex for operations)
|
**Production Ready:** [CRITICAL] **NO** (too complex for operations)
|
||||||
|
|
||||||
### Option 4: Kubernetes (FUTURE)
|
### Option 4: Kubernetes (FUTURE)
|
||||||
|
|
||||||
@@ -265,10 +265,10 @@ https://your-server:5001
|
|||||||
**Problem:** Which one do I follow?
|
**Problem:** Which one do I follow?
|
||||||
|
|
||||||
**Solution:** Create decision tree:
|
**Solution:** Create decision tree:
|
||||||
- Developer on Windows → LOCAL-DEV-SETUP.md
|
- Developer on Windows -> LOCAL-DEV-SETUP.md
|
||||||
- Ops deploying to prod → (DOESN'T EXIST YET - need simplified guide)
|
- Ops deploying to prod -> (DOESN'T EXIST YET - need simplified guide)
|
||||||
- Custom development → DOCKER-SETUP.md
|
- Custom development -> DOCKER-SETUP.md
|
||||||
- Backup questions → scripts/backup/README.md
|
- Backup questions -> scripts/backup/README.md
|
||||||
|
|
||||||
### Missing Guides
|
### Missing Guides
|
||||||
|
|
||||||
@@ -279,12 +279,12 @@ https://your-server:5001
|
|||||||
|
|
||||||
## What We Did Today (Honest Assessment)
|
## What We Did Today (Honest Assessment)
|
||||||
|
|
||||||
✅ **Merged wilm→wj** - 98 commits, wilm features now in wj
|
[OK] **Merged wilm->wj** - 98 commits, wilm features now in wj
|
||||||
✅ **Fixed local dev** - `run_dev.py` works for Windows development
|
[OK] **Fixed local dev** - `run_dev.py` works for Windows development
|
||||||
✅ **Documented Docker** - Comprehensive but too complex
|
[OK] **Documented Docker** - Comprehensive but too complex
|
||||||
✅ **Added backup docs** - System works but needs expertise
|
[OK] **Added backup docs** - System works but needs expertise
|
||||||
⚠️ **Docker "production ready"** - Technically works, operationally complex
|
[WARN] **Docker "production ready"** - Technically works, operationally complex
|
||||||
❌ **Simple deployment** - Still requires too many manual steps
|
[FAIL] **Simple deployment** - Still requires too many manual steps
|
||||||
|
|
||||||
## Next Steps (Priority Order)
|
## Next Steps (Priority Order)
|
||||||
|
|
||||||
@@ -301,7 +301,7 @@ https://your-server:5001
|
|||||||
- Document any issues
|
- Document any issues
|
||||||
|
|
||||||
3. **Create deployment decision tree**
|
3. **Create deployment decision tree**
|
||||||
- Flowchart: what's my situation → which method to use
|
- Flowchart: what's my situation -> which method to use
|
||||||
- Don't make users read 4 docs to figure out where to start
|
- Don't make users read 4 docs to figure out where to start
|
||||||
|
|
||||||
### Short Term (Next Sprint)
|
### Short Term (Next Sprint)
|
||||||
|
|||||||
@@ -33,10 +33,10 @@ cd C:\ProgramData\ShopDB\EndpointAgent
|
|||||||
|
|
||||||
## Why HTTP is OK Here
|
## Why HTTP is OK Here
|
||||||
|
|
||||||
- ✅ VM and host on same physical machine (Hyper-V)
|
- [X] VM and host on same physical machine (Hyper-V)
|
||||||
- ✅ Traffic never leaves the host
|
- [X] Traffic never leaves the host
|
||||||
- ✅ Hyper-V internal network is isolated
|
- [X] Hyper-V internal network is isolated
|
||||||
- ✅ No external network exposure
|
- [X] No external network exposure
|
||||||
|
|
||||||
For production multi-machine deployment, use HTTPS with proper certificates.
|
For production multi-machine deployment, use HTTPS with proper certificates.
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ VIOLATIONS=0
|
|||||||
|
|
||||||
EXCLUDES=(
|
EXCLUDES=(
|
||||||
--exclude-dir=venv
|
--exclude-dir=venv
|
||||||
|
--exclude-dir=venv-quick
|
||||||
--exclude-dir=env
|
--exclude-dir=env
|
||||||
--exclude-dir=.venv
|
--exclude-dir=.venv
|
||||||
--exclude-dir=node_modules
|
--exclude-dir=node_modules
|
||||||
@@ -39,6 +40,8 @@ EXCLUDES=(
|
|||||||
# source, never committed, and free to hold pasted logs full of em-dashes
|
# source, never committed, and free to hold pasted logs full of em-dashes
|
||||||
# and arrows - grep does not consult .gitignore, so exclude it explicitly.
|
# and arrows - grep does not consult .gitignore, so exclude it explicitly.
|
||||||
--exclude-dir=devnotes
|
--exclude-dir=devnotes
|
||||||
|
--exclude=findings.sarif
|
||||||
|
--exclude=report.md
|
||||||
)
|
)
|
||||||
|
|
||||||
# Root-level gitignored scratch: review writeups and chat handoffs, free to hold
|
# Root-level gitignored scratch: review writeups and chat handoffs, free to hold
|
||||||
|
|||||||
@@ -622,18 +622,22 @@ def finalize_restore():
|
|||||||
'Could not re-arm failed upgrade: ' + str(error),
|
'Could not re-arm failed upgrade: ' + str(error),
|
||||||
http_code=500)
|
http_code=500)
|
||||||
|
|
||||||
# Try to claim a pending upgrade
|
# Check if there's a pending upgrade sentinel (but don't claim it)
|
||||||
setname = _claim_upgrade()
|
# The watcher/background worker will claim and run it
|
||||||
if not setname:
|
sentinel = os.path.join(root, UPGRADE_SENTINEL)
|
||||||
return success_response(
|
if os.path.isfile(sentinel):
|
||||||
data={'queued': False},
|
try:
|
||||||
message='No restore is awaiting schema upgrade.')
|
with open(sentinel, 'r', encoding='utf-8') as handle:
|
||||||
|
setname = handle.read().strip()
|
||||||
|
return success_response(
|
||||||
|
data={'queued': True, 'snapshotid': setname},
|
||||||
|
message='Schema upgrade queued.')
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
|
||||||
# Queue the upgrade but don't run it inline (it can outlast the request timeout)
|
|
||||||
# The watcher or a background worker will pick up the claim and run it
|
|
||||||
return success_response(
|
return success_response(
|
||||||
data={'queued': True, 'snapshotid': setname},
|
data={'queued': False},
|
||||||
message='Schema upgrade queued.')
|
message='No restore is awaiting schema upgrade.')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user