Files
pxe-server/webapp/services/fs.py
cproudlock c16a4f23b4 webapp: extract service layer (config.py + services/) from app.py
Phase 1a of a multi-session refactor toward a clean blueprint
structure. Pulls the helper code that lived alongside the routes in
the 1621-line app.py into focused modules. app.py is now 625 lines
of mostly routes plus a small Flask wiring header. Behaviour is
unchanged: smoke-tested against the 8 main GET routes (200 OK).

New modules:

- config.py            env vars + IMAGE_TYPES + FRIENDLY_NAMES +
                       SHARED_DEPLOY_* taxonomy + unattend XML
                       namespaces.
- services/audit.py    audit log file handler + audit() helper.
- services/csrf.py     session CSRF token + before_request validator
                       wired via init_csrf(app).
- services/fs.py       image_root / deploy_path / unattend_path /
                       control_path / tools_path + load_json /
                       save_json + resolve_destination.
- services/system.py   service_status / find_usb_mounts /
                       find_upload_sources.
- services/images.py   image_status + load_image_config.
- services/deploy.py   import_deploy + _merge_tree +
                       _replace_with_symlink + allowed_import_source.
- services/unattend.py parse_unattend / build_unattend_xml /
                       extract_form_data and the qn / qwcm / settings
                       pass helpers.
- services/wim.py      extract_startnet / update_startnet / list_files
                       wrapping wimextract / wimupdate / wimdir.

Endpoint names kept stable (dashboard, clonezilla_backups, etc.) so
existing url_for(...) calls in templates are unchanged. Phase 1b
(Flask blueprints with ".endpoint" naming) deferred to a future
session because it requires updating ~30 url_for sites in templates
and is mostly cosmetic.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 18:25:32 -04:00

76 lines
2.2 KiB
Python

"""Filesystem path helpers + tiny JSON load/save utilities.
All paths are derived from ``config.SAMBA_SHARE`` so swapping the share
root only requires changing one env var.
"""
import json
import os
import config
def image_root(image_type):
"""Return the root directory for an image type."""
return os.path.join(config.SAMBA_SHARE, image_type)
def deploy_path(image_type):
"""Return the Deploy directory for an image type."""
return os.path.join(config.SAMBA_SHARE, image_type, "Deploy")
def unattend_path(image_type):
"""Return the unattend.xml path for an image type."""
return os.path.join(deploy_path(image_type), "FlatUnattendW10.xml")
def control_path(image_type):
"""Return the Deploy/Control directory for an image type."""
return os.path.join(deploy_path(image_type), "Control")
def tools_path(image_type):
"""Return the Tools directory for an image type."""
return os.path.join(config.SAMBA_SHARE, image_type, "Tools")
def load_json(filepath):
"""Parse a JSON file and return its contents, or [] on failure."""
try:
with open(filepath, "r", encoding="utf-8-sig") as fh:
return json.load(fh)
except (OSError, json.JSONDecodeError):
return []
def save_json(filepath, data):
"""Write data as pretty-printed JSON, creating parent dirs as needed."""
os.makedirs(os.path.dirname(filepath), exist_ok=True)
with open(filepath, "w", encoding="utf-8") as fh:
json.dump(data, fh, indent=2, ensure_ascii=False)
fh.write("\n")
def resolve_destination(dest_dir, image_type):
"""Convert a Windows ``*destinationdir*`` path to a Linux filesystem path.
Replaces the placeholder + backslashes, prepends
``SAMBA_SHARE/image_type/``, then resolves symlinks so shared dirs
are followed.
"""
if not dest_dir:
return ""
path = dest_dir
lower = path.lower()
idx = lower.find("*destinationdir*")
if idx != -1:
path = path[idx + len("*destinationdir*"):]
path = path.replace("\\", "/").lstrip("/")
full = os.path.join(config.SAMBA_SHARE, image_type, path)
try:
full = os.path.realpath(full)
except OSError:
pass
return full