Refactor code structure for improved readability and maintainability

This commit is contained in:
2026-05-13 12:01:11 +02:00
parent 50c0b4d012
commit f96c656385
22 changed files with 4352 additions and 4032 deletions

123
routes/upload_routes.py Normal file
View File

@@ -0,0 +1,123 @@
"""PiCopy Blueprint: /api/upload/*."""
import subprocess
import uuid as _uuid_mod
from flask import Blueprint, jsonify, request
from picopy.config import load_cfg, save_cfg
from picopy.upload import (
upload_state, upload_lock,
configure_smb_remote, delete_remote, test_remote,
_rclone_obscure, RCLONE_CONF as _RCLONE_CONF,
)
upload_bp = Blueprint('upload', __name__)
@upload_bp.route('/api/upload/targets', methods=['GET'])
def r_upload_list():
return jsonify(load_cfg().get('upload_targets', []))
@upload_bp.route('/api/upload/targets', methods=['POST'])
def r_upload_add():
data = request.get_json(force=True)
cfg = load_cfg()
tid = data.get('id') or _uuid_mod.uuid4().hex[:8]
ctype = data.get('type', 'smb')
if ctype != 'smb':
return jsonify(error='Nur SMB/NAS wird unterstützt'), 400
ok, err = configure_smb_remote(
tid, data.get('host', ''), data.get('share', ''),
data.get('user', ''), data.get('pass', ''))
if not ok:
return jsonify(error=f'rclone: {err}'), 500
# Credentials direkt im Entry speichern (für Connection-String bei Upload)
obscured_pw = _rclone_obscure(data.get('pass', '')) if data.get('pass') else ''
entry = {
'id': tid, 'type': ctype,
'name': data.get('name', ctype),
'dest_path': data.get('dest_path', 'PiCopy'),
'enabled': True,
'smb_host': data.get('host', ''),
'smb_share': data.get('share', ''),
'smb_user': data.get('user', ''),
'smb_pass': obscured_pw,
}
targets = [t for t in cfg.get('upload_targets', []) if t['id'] != tid]
targets.append(entry)
cfg['upload_targets'] = targets
save_cfg(cfg)
return jsonify(ok=True, id=tid)
@upload_bp.route('/api/upload/targets/<tid>', methods=['DELETE'])
def r_upload_del(tid):
cfg = load_cfg()
cfg['upload_targets'] = [t for t in cfg.get('upload_targets', []) if t['id'] != tid]
save_cfg(cfg)
delete_remote(tid)
return jsonify(ok=True)
@upload_bp.route('/api/upload/browse', methods=['POST'])
def r_upload_browse():
"""Listet SMB-Freigaben eines Servers ohne gespeicherte Config (rclone connection string)."""
data = request.get_json(force=True)
host = data.get('host', '').strip()
user = data.get('user', '').strip()
pw = data.get('pass', '')
if not host:
return jsonify(error='Server-Adresse fehlt'), 400
conn = f':smb,host={host}'
if user:
conn += f',user={user}'
if pw:
try:
obscured = _rclone_obscure(pw)
conn += f',pass={obscured}'
except Exception:
pass
conn += ':'
r = subprocess.run(
['rclone', '--config', str(_RCLONE_CONF), 'lsd', conn],
capture_output=True, text=True, timeout=15
)
if r.returncode != 0:
lines = r.stderr.strip().splitlines()
err = lines[-1] if lines else 'Verbindung fehlgeschlagen'
return jsonify(error=err), 400
shares = [line.strip().split()[-1] for line in r.stdout.splitlines() if line.strip()]
return jsonify(shares=shares)
@upload_bp.route('/api/upload/targets/<tid>/toggle', methods=['POST'])
def r_upload_toggle(tid):
cfg = load_cfg()
for t in cfg.get('upload_targets', []):
if t['id'] == tid:
t['enabled'] = not t.get('enabled', True)
break
save_cfg(cfg)
return jsonify(ok=True)
@upload_bp.route('/api/upload/targets/<tid>/test', methods=['POST'])
def r_upload_test(tid):
from picopy.config import log
try:
ok, err = test_remote(tid)
except Exception as e:
log.exception('upload test failed')
ok, err = False, str(e)
return jsonify(ok=ok, error=err)
@upload_bp.route('/api/upload/status')
def r_upload_status():
with upload_lock:
return jsonify(dict(upload_state))