feat: SMB-Freigaben-API hinzugefügt und Versionsnummer auf 1.0.34 erhöht
This commit is contained in:
127
app.py
127
app.py
@@ -1192,6 +1192,37 @@ def r_upload_del(tid):
|
||||
return jsonify(ok=True)
|
||||
|
||||
|
||||
@app.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)
|
||||
|
||||
|
||||
@app.route('/api/upload/targets/<tid>/toggle', methods=['POST'])
|
||||
def r_upload_toggle(tid):
|
||||
cfg = load_cfg()
|
||||
@@ -1857,19 +1888,41 @@ body{background:var(--bg);color:var(--txt);font-family:-apple-system,BlinkMacSys
|
||||
<button class="btn" onclick="utToggleForm()" id="ut-add-btn">+ NAS-Ziel hinzufügen</button>
|
||||
|
||||
<div id="ut-form" class="add-panel" style="display:none">
|
||||
<div class="sec" style="margin-top:0">SMB / Netzlaufwerk</div>
|
||||
<div class="field"><label>Name</label><input type="text" id="ut-name" placeholder="z.B. Heimserver NAS"></div>
|
||||
<div class="field"><label>Ziel-Ordner auf dem NAS</label><input type="text" id="ut-dest" value="PiCopy" placeholder="PiCopy"></div>
|
||||
|
||||
<!-- Schritt 1: Verbindungsdaten -->
|
||||
<div id="ut-step1">
|
||||
<div class="sec" style="margin-top:0">Schritt 1 – Server-Verbindung</div>
|
||||
<div class="field"><label>Server (IP / Hostname)</label>
|
||||
<input type="text" id="ut-host" placeholder="192.168.1.100 oder nas.local" autocomplete="off"></div>
|
||||
<div style="display:grid;grid-template-columns:1fr 1fr;gap:.5rem">
|
||||
<div class="field"><label>Server (IP / Hostname)</label><input type="text" id="ut-host" placeholder="192.168.1.100"></div>
|
||||
<div class="field"><label>Freigabe-Name</label><input type="text" id="ut-share" placeholder="backup"></div>
|
||||
<div class="field"><label>Benutzer</label><input type="text" id="ut-user" placeholder="tobias"></div>
|
||||
<div class="field"><label>Passwort</label><input type="password" id="ut-pass" placeholder="(leer = kein Passwort)"></div>
|
||||
<div class="field"><label>Benutzer</label>
|
||||
<input type="text" id="ut-user" placeholder="(leer = anonym)" autocomplete="off"></div>
|
||||
<div class="field"><label>Passwort</label>
|
||||
<input type="password" id="ut-pass" placeholder="(leer = kein Passwort)" autocomplete="off"></div>
|
||||
</div>
|
||||
<div class="btn-row">
|
||||
<button class="btn pri" id="ut-connect-btn" onclick="utConnect()">🔗 Verbinden & Freigaben laden</button>
|
||||
<button class="btn ghost" onclick="utToggleForm()">Abbrechen</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Schritt 2: Freigabe wählen (nach erfolgreicher Verbindung) -->
|
||||
<div id="ut-step2" style="display:none">
|
||||
<div class="sec" style="margin-top:0">Schritt 2 – Freigabe & Details</div>
|
||||
<div class="field"><label>Freigabe wählen</label>
|
||||
<select id="ut-share-sel" style="width:100%"></select></div>
|
||||
<div style="display:grid;grid-template-columns:1fr 1fr;gap:.5rem">
|
||||
<div class="field"><label>Name (Anzeigename)</label>
|
||||
<input type="text" id="ut-name" placeholder="z.B. Heimserver NAS"></div>
|
||||
<div class="field"><label>Ziel-Ordner auf dem NAS</label>
|
||||
<input type="text" id="ut-dest" placeholder="PiCopy" value="PiCopy"></div>
|
||||
</div>
|
||||
<div class="btn-row">
|
||||
<button class="btn pri" onclick="utSave()">✓ Speichern & Verbindung testen</button>
|
||||
<button class="btn ghost" onclick="utToggleForm()">Abbrechen</button>
|
||||
<button class="btn ghost" onclick="utBack()">← Zurück</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="ut-form-flash" class="flash" style="margin-top:.4rem"></div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -2239,8 +2292,7 @@ async function saveAP(){
|
||||
// -- Upload-Ziele --------------------------------------------------------------
|
||||
const UT_ICONS={smb:'🖧',onedrive:'☁',drive:'📄',dropbox:'📦'};
|
||||
const UT_LABELS={smb:'SMB/NAS',onedrive:'OneDrive',drive:'Google Drive',dropbox:'Dropbox'};
|
||||
const UT_CMD={onedrive:'rclone authorize "onedrive"',drive:'rclone authorize "drive"',dropbox:'rclone authorize "dropbox"'};
|
||||
let utType='smb', utTargets=[];
|
||||
let utTargets=[], _utConn={};
|
||||
|
||||
async function loadUTs(){utTargets=await api('/upload/targets');renderUTs();}
|
||||
function renderUTs(){
|
||||
@@ -2264,41 +2316,50 @@ function renderUTs(){
|
||||
function utToggleForm(){
|
||||
const f=$('ut-form'),b=$('ut-add-btn'),show=f.style.display==='none';
|
||||
f.style.display=show?'block':'none';
|
||||
b.innerHTML=show?'✕ Abbrechen':'+ Ziel hinzufügen';
|
||||
if(show){utSelectType('smb',document.querySelector('.sel-opt'));}
|
||||
b.innerHTML=show?'✕ Abbrechen':'+ NAS-Ziel hinzufügen';
|
||||
if(show){
|
||||
$('ut-step1').style.display=''; $('ut-step2').style.display='none';
|
||||
['ut-host','ut-user','ut-pass','ut-name'].forEach(id=>{$(id).value='';});
|
||||
$('ut-dest').value='PiCopy';
|
||||
$('ut-form-flash').style.display='none';
|
||||
_utConn={};
|
||||
}
|
||||
}
|
||||
function utSelectType(type,el){
|
||||
utType=type;
|
||||
document.querySelectorAll('.sel-opt').forEach(b=>b.classList.remove('on'));
|
||||
el.classList.add('on');
|
||||
$('ut-smb-fields').style.display=type==='smb'?'block':'none';
|
||||
$('ut-cloud-fields').style.display=type!=='smb'?'block':'none';
|
||||
if(UT_CMD[type]) $('ut-auth-cmd').textContent=UT_CMD[type];
|
||||
async function utConnect(){
|
||||
const host=$('ut-host').value.trim();
|
||||
if(!host){flash('ut-form-flash','err','Server-Adresse fehlt');return;}
|
||||
const btn=$('ut-connect-btn');
|
||||
btn.disabled=true; btn.textContent='Verbinde...';
|
||||
$('ut-form-flash').style.display='none';
|
||||
const r=await api('/upload/browse','POST',{
|
||||
host, user:$('ut-user').value.trim(), pass:$('ut-pass').value
|
||||
});
|
||||
btn.disabled=false; btn.innerHTML='🔗 Verbinden & Freigaben laden';
|
||||
if(r.error){flash('ut-form-flash','err','✗ '+r.error);return;}
|
||||
if(!r.shares||!r.shares.length){flash('ut-form-flash','warn','Verbunden, aber keine Freigaben gefunden');return;}
|
||||
_utConn={host, user:$('ut-user').value.trim(), pass:$('ut-pass').value};
|
||||
$('ut-share-sel').innerHTML=r.shares.map(s=>`<option value="${s}">${s}</option>`).join('');
|
||||
if(!$('ut-name').value) $('ut-name').value=host;
|
||||
$('ut-step1').style.display='none'; $('ut-step2').style.display='';
|
||||
}
|
||||
function utCopyCmd(){
|
||||
navigator.clipboard?.writeText($('ut-auth-cmd').textContent);
|
||||
const b=document.querySelector('[onclick="utCopyCmd()"]');
|
||||
b.textContent='✓'; setTimeout(()=>b.textContent='📋',1500);
|
||||
function utBack(){
|
||||
$('ut-step1').style.display=''; $('ut-step2').style.display='none';
|
||||
$('ut-form-flash').style.display='none';
|
||||
}
|
||||
async function utSave(){
|
||||
const name=$('ut-name').value.trim(), dest=$('ut-dest').value.trim()||'PiCopy';
|
||||
const share=$('ut-share-sel').value;
|
||||
if(!name){flash('ut-form-flash','err','Name fehlt');return;}
|
||||
let body={type:utType,name,dest_path:dest};
|
||||
if(utType==='smb'){
|
||||
body.host=$('ut-host').value.trim(); body.share=$('ut-share').value.trim();
|
||||
body.user=$('ut-user').value.trim(); body.pass=$('ut-pass').value;
|
||||
if(!body.host||!body.share){flash('ut-form-flash','err','Server und Freigabe sind Pflicht');return;}
|
||||
} else {
|
||||
body.token=$('ut-token').value.trim();
|
||||
if(!body.token){flash('ut-form-flash','err','Token fehlt');return;}
|
||||
}
|
||||
if(!share){flash('ut-form-flash','err','Bitte eine Freigabe wählen');return;}
|
||||
const body={type:'smb',name,dest_path:dest,share,
|
||||
host:_utConn.host, user:_utConn.user, pass:_utConn.pass};
|
||||
flash('ut-form-flash','ok','Speichere...');
|
||||
const r=await api('/upload/targets','POST',body);
|
||||
if(r.error){flash('ut-form-flash','err',r.error);return;}
|
||||
flash('ut-form-flash','ok','Teste Verbindung...');
|
||||
const t=await api('/upload/targets/'+r.id+'/test','POST');
|
||||
if(t.ok){flash('ut-form-flash','ok','✓ Verbindung OK - Lesen & Schreiben erfolgreich');utToggleForm();await loadUTs();}
|
||||
else flash('ut-form-flash','err','✗ ' + (t.error||'Test fehlgeschlagen'));
|
||||
else flash('ut-form-flash','err','✗ '+(t.error||'Test fehlgeschlagen'));
|
||||
}
|
||||
async function utTest(id){
|
||||
const btn=$('ut-test-'+id), res=$('ut-test-result-'+id);
|
||||
|
||||
@@ -1 +1 @@
|
||||
1.0.33
|
||||
1.0.34
|
||||
Reference in New Issue
Block a user