Printer assignment end to end, and a container that keeps a site's files

Assign printers to a MACHINE and let whichever PC controls it install them, with
the observed half kept strictly apart from the assigned half: a collector report
can never write an assignment row. Adoption is explicit. Adds a singular
relationship type so defaultprinter can mean exactly one, client scripts that
converge a bay's queues and correct a drifted one in place, and a wave gate so a
fleet-wide driver rollout cannot stampede its own share.

On the container side: the compose stack never persisted the instance directory,
so a rebuild discarded plugins.json and every upload. It is a volume now, the
image ships instance/ owned by the app user so the mountpoint is writable, and
the database moves to MySQL 8.4 LTS, which the Windows runbook has recommended
since 8.0 went end of life. The image also could not build at all: the frontend
stage never copied the plugin staging script it runs.

Adds a topology migration guide covering IIS to Docker and back.
This commit is contained in:
2026-08-20 07:46:35 -04:00
parent 09e9d5f047
commit d267813999
45 changed files with 6475 additions and 56 deletions

View File

@@ -516,12 +516,43 @@ def seed_reference_data():
for at in adr_types:
if not _lookup_binary(at['relationshiptype']):
db.session.add(RelationshipType(**at))
# Printer assignment edges. usesprinter = "this printer is installed here",
# defaultprinter = which of them is the default. Assignment lives on the
# MACHINE asset and reaches whichever PC controls it through the controls
# rail seeded below, so a reimaged PC gets its printers back with no
# restore step. Created before the rails: _seed_propagation no-ops in
# silence when either type is missing.
printer_types = [
{'relationshiptype': 'usesprinter',
'description': 'Asset to a printer installed on it (ADR-001)',
'isdirectional': True},
{'relationshiptype': 'defaultprinter',
'description': 'PC to its default printer (installer preselect, ADR-001)',
'isdirectional': True,
# One default per asset. The unique constraint is (source, target,
# type), so two DIFFERENT defaults are two valid rows and the resolver
# then takes the oldest - the new default silently loses. Core's
# create path replaces instead, driven by this flag.
'issingular': True},
]
for pt in printer_types:
existing = _lookup_binary(pt['relationshiptype'])
if not existing:
db.session.add(RelationshipType(**pt))
elif pt.get('issingular') and not existing.issingular:
# A site seeded before the flag existed keeps its row and gains the
# rule; without this the upgrade leaves the old silent behaviour.
existing.issingular = True
db.session.flush()
# Seed `controls` propagation rails as M:N rows. controls -> partof
# (declared; directional rail, not consumed yet) and controls -> Dualpath
# (consumed; a dual-bay pair shares one controller so both bays carry
# controls). Idempotent, resolved by name, skipped if a type is missing.
# Seed propagation rails as M:N rows. controls -> partof (declared;
# directional rail, not consumed yet) and controls -> Dualpath (consumed;
# a dual-bay pair shares one controller so both bays carry controls).
# The two printer rails are READ-TIME only: the create-time fan-out skips
# directional through-types, and controls is directional, so a PC's own
# rows keep beating what it inherits from the machine it controls.
# Idempotent, resolved by name, skipped if a type is missing.
from shopdb.core.models.relationship import RelationshipTypePropagation
def _seed_propagation(sourcename, throughname):
@@ -541,15 +572,8 @@ def seed_reference_data():
_seed_propagation('controls', 'partof')
_seed_propagation('controls', 'Dualpath')
# Default-printer link: a PC asset -> its default printer asset. Read by the
# printer-installer endpoint (parity with classic apipcdefaultprinter.asp).
# Attribute-style edge, not a position rail, so no propagation.
if not _lookup_binary('defaultprinter'):
db.session.add(RelationshipType(
relationshiptype='defaultprinter',
description='PC to its default printer (installer preselect, ADR-001)'
))
_seed_propagation('usesprinter', 'controls')
_seed_propagation('defaultprinter', 'controls')
db.session.commit()
click.echo(click.style("Reference data seeded.", fg='green'))