Establishes the safety net required before any structural refactor. Tests (tests/): - conftest.py rewritten for Flask-SQLAlchemy 3.x (drop-recreate per test, StaticPool-shared in-memory SQLite, admin_user + auth_headers fixtures). Removes deprecated db.create_scoped_session pattern. - test_smoke.py: 8 baseline tests (app boot, JWT login valid+invalid, protected routes, paginated response shape, plugin auto-discovery). - test_security_config.py: 7 tests pinning ProductionConfig.validate failure modes (missing/dev SECRET_KEY, missing JWT_SECRET_KEY, missing DATABASE_URL, wildcard CORS, empty CORS) and one happy-path. Production hardening (shopdb/config.py, shopdb/__init__.py): - ProductionConfig.validate() raises ConfigError on missing or insecure SECRET_KEY, JWT_SECRET_KEY, DATABASE_URL, CORS_ORIGINS. No silent fallback to dev defaults in production. - create_app invokes validate() when config_name == 'production'. - CORS_ORIGINS default no longer wildcard; defaults to localhost Vite dev origin. - Drop os.path.exists probe in serve_frontend (path-traversal risk surface). send_from_directory handles safe-join + 404 itself. - Replace User.query.get with db.session.get (SQLAlchemy 2.0 API). TestingConfig (shopdb/config.py): - Add StaticPool + check_same_thread connect_args so SQLite in-memory is shared across the test session. Index dedup (plugins/printers/models/printer_extension.py): - Rename idx_printer_windowsname -> idx_printerdata_windowsname. Two model classes (Printer, PrinterData) declared the same index name; SQLite enforces global index uniqueness even across tables. Per CONTRIBUTING.md naming convention, indexes follow idx_<table>_<column>. Dependency pinning (requirements.in, requirements.txt): - requirements.in holds the loose source pins (the human-edited file). - requirements.txt is now a uv-compiled lockfile (every transitive dep pinned to an exact version). Reproducible builds. Run `uv pip compile requirements.in -o requirements.txt` to refresh. Test count: 0 -> 15 passing. All naming/style checks still green. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
59 lines
1.6 KiB
Python
59 lines
1.6 KiB
Python
"""PrinterData model - printer-specific fields linked to machines."""
|
|
|
|
from shopdb.extensions import db
|
|
from shopdb.core.models.base import BaseModel
|
|
|
|
|
|
class PrinterData(BaseModel):
|
|
"""
|
|
Printer-specific data linked to Machine table.
|
|
|
|
Printers are stored in the machines table (machinetype.category = 'Printer').
|
|
This table only holds printer-specific fields not in machines.
|
|
|
|
IP address is stored in the communications table.
|
|
Zabbix data is queried in real-time via API (not cached here).
|
|
"""
|
|
__tablename__ = 'printerdata'
|
|
|
|
id = db.Column(db.Integer, primary_key=True)
|
|
|
|
# Link to machine
|
|
machineid = db.Column(
|
|
db.Integer,
|
|
db.ForeignKey('machines.machineid', ondelete='CASCADE'),
|
|
unique=True,
|
|
nullable=False,
|
|
index=True
|
|
)
|
|
|
|
# Windows/Network naming
|
|
windowsname = db.Column(
|
|
db.String(255),
|
|
comment='Windows printer name (e.g., \\\\server\\printer)'
|
|
)
|
|
sharename = db.Column(
|
|
db.String(100),
|
|
comment='CSF/share name'
|
|
)
|
|
|
|
# Installation
|
|
iscsf = db.Column(db.Boolean, default=False, comment='Is CSF printer')
|
|
installpath = db.Column(db.String(255), comment='Driver install path')
|
|
|
|
# Printer PIN (for secure print)
|
|
pin = db.Column(db.String(20))
|
|
|
|
# Relationship
|
|
machine = db.relationship(
|
|
'Machine',
|
|
backref=db.backref('printerdata', uselist=False, lazy='joined')
|
|
)
|
|
|
|
__table_args__ = (
|
|
db.Index('idx_printerdata_windowsname', 'windowsname'),
|
|
)
|
|
|
|
def __repr__(self):
|
|
return f"<PrinterData machineid={self.machineid}>"
|