feat(samba): Megosztas page + guarded folder picker (R-7 slice 1, Part 3)
New top-nav category with the Halozati megosztas page: enable/server-name card, household password, shares table (Nev/Mappa/Irasvedett/Felhomentes/Torles), and a create flow (new folder under <storage>/shares or an existing folder via the browse modal). Every customer path goes through sharingResolvePath: absolute -> EvalSymlinks -> containment in a registered live storage root -> deny-listed system subtree check -> is-a-directory. Refusals are UNIFORM so the picker is never a filesystem oracle. Deny-list derived from ProtectedHDDPaths (provably a subset); the drive root is an exact-match denial so user-data folders under it stay shareable. samba infra metadata + i-share icon. Gates green.
This commit is contained in:
@@ -12,6 +12,7 @@
|
||||
<symbol id="i-external-link" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M15 3h6v6" /> <path d="M10 14 21 3" /> <path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6" /></symbol>
|
||||
<symbol id="i-shield" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z" /></symbol>
|
||||
<symbol id="i-server" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="20" height="8" x="2" y="2" rx="2" ry="2" /> <rect width="20" height="8" x="2" y="14" rx="2" ry="2" /> <line x1="6" x2="6.01" y1="6" y2="6" /> <line x1="6" x2="6.01" y1="18" y2="18" /></symbol>
|
||||
<symbol id="i-share" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="18" cy="5" r="3" /> <circle cx="6" cy="12" r="3" /> <circle cx="18" cy="19" r="3" /> <line x1="8.59" x2="15.42" y1="13.51" y2="17.49" /> <line x1="15.41" x2="8.59" y1="6.51" y2="10.49" /></symbol>
|
||||
<symbol id="i-layout-grid" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="7" height="7" x="3" y="3" rx="1" /> <rect width="7" height="7" x="14" y="3" rx="1" /> <rect width="7" height="7" x="14" y="14" rx="1" /> <rect width="7" height="7" x="3" y="14" rx="1" /></symbol>
|
||||
<symbol id="i-settings" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9.671 4.136a2.34 2.34 0 0 1 4.659 0 2.34 2.34 0 0 0 3.319 1.915 2.34 2.34 0 0 1 2.33 4.033 2.34 2.34 0 0 0 0 3.831 2.34 2.34 0 0 1-2.33 4.033 2.34 2.34 0 0 0-3.319 1.915 2.34 2.34 0 0 1-4.659 0 2.34 2.34 0 0 0-3.32-1.915 2.34 2.34 0 0 1-2.33-4.033 2.34 2.34 0 0 0 0-3.831A2.34 2.34 0 0 1 6.35 6.051a2.34 2.34 0 0 0 3.319-1.915" /> <circle cx="12" cy="12" r="3" /></symbol>
|
||||
<symbol id="i-bell" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10.268 21a2 2 0 0 0 3.464 0" /> <path d="M3.262 15.326A1 1 0 0 0 4 17h16a1 1 0 0 0 .74-1.673C19.41 13.956 18 12.499 18 8A6 6 0 0 0 6 8c0 4.499-1.411 5.956-2.738 7.326" /></symbol>
|
||||
|
||||
@@ -67,6 +67,11 @@
|
||||
<li><a href="/backups/restore" class="{{if eq .Page "backups-restore"}}active{{end}}">Visszaállítás</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="/sharing" class="{{if eq .Page "sharing"}}active{{end}}"><svg class="ico"><use href="#i-share"/></svg>Megosztás</a>
|
||||
<ul class="nav-links nav-links-sub nav-links-nested">
|
||||
<li><a href="/sharing" class="{{if eq .Page "sharing"}}active{{end}}">Hálózati megosztás</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="/monitoring" class="{{if eq .Page "monitoring"}}active{{end}}"><svg class="ico"><use href="#i-cpu"/></svg>Rendszermonitor</a></li>
|
||||
<li><a href="/debug" class="{{if eq .Page "debug"}}active{{end}}"><svg class="ico"><use href="#i-wrench"/></svg>Debug</a></li>
|
||||
</ul>
|
||||
|
||||
@@ -0,0 +1,243 @@
|
||||
{{define "sharing"}}
|
||||
{{template "layout_start" .}}
|
||||
|
||||
<div class="page-header">
|
||||
<h2>Hálózati megosztás</h2>
|
||||
</div>
|
||||
|
||||
{{if .Flash}}
|
||||
<div class="alert alert-success">{{.Flash}}</div>
|
||||
{{end}}
|
||||
|
||||
<div class="settings-card">
|
||||
<p class="settings-card-desc">
|
||||
A megosztott mappák a Windows Intézőben és a Mac Finderben jelennek meg, mintha egy hálózati
|
||||
meghajtó lennének. A megosztás <strong>csak az otthoni hálózaton</strong> érhető el — az
|
||||
internetről nem.
|
||||
</p>
|
||||
|
||||
<form method="POST" action="/sharing/enable">
|
||||
{{.CSRFField}}
|
||||
<div class="form-group">
|
||||
<label>
|
||||
<input type="checkbox" name="enabled" {{if .SMBEnabled}}checked{{end}}>
|
||||
Megosztás engedélyezése
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="server_name">Kiszolgáló neve</label>
|
||||
<input type="text" id="server_name" name="server_name" class="form-control mono"
|
||||
value="{{.SMBServerName}}" maxlength="15" autocomplete="off">
|
||||
<div class="form-hint">
|
||||
Ezen a néven jelenik meg a gép a hálózaton (<span class="mono">\\{{.SMBServerName}}</span>).
|
||||
Legfeljebb 15 karakter: betű, szám, kötőjel, aláhúzás.
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-actions">
|
||||
<button type="submit" class="btn btn-primary">Mentés</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div class="form-group">
|
||||
<span>Állapot:</span>
|
||||
{{if .SMBRunning}}
|
||||
<span class="badge badge-ok">fut</span>
|
||||
{{else}}
|
||||
<span class="badge badge-neutral">áll</span>
|
||||
{{end}}
|
||||
{{if and .SMBEnabled (not .SMBUserSet)}}
|
||||
<span class="badge badge-warn">először adj meg jelszót</span>
|
||||
{{end}}
|
||||
</div>
|
||||
<div class="form-hint">
|
||||
Egyes hálózatokon a gép késve jelenik meg az eszközlistában. A
|
||||
<span class="mono">\\{{.SMBServerName}}</span> cím beírása a címsorba ilyenkor is működik.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="settings-card">
|
||||
<h3>Megosztási jelszó beállítása</h3>
|
||||
<p class="settings-card-desc">
|
||||
Egy közös jelszó tartozik a háztartáshoz. A csatlakozáskor felhasználónévnek add meg:
|
||||
<span class="mono">felhom</span>.
|
||||
</p>
|
||||
<form method="POST" action="/sharing/password">
|
||||
{{.CSRFField}}
|
||||
<div class="form-group">
|
||||
<label for="smb_password">Új jelszó</label>
|
||||
<input type="password" id="smb_password" name="smb_password" class="form-control"
|
||||
autocomplete="new-password" minlength="8" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="smb_password_confirm">Jelszó megerősítése</label>
|
||||
<input type="password" id="smb_password_confirm" name="smb_password_confirm" class="form-control"
|
||||
autocomplete="new-password" minlength="8" required>
|
||||
</div>
|
||||
<div class="form-actions">
|
||||
<button type="submit" class="btn btn-primary">Jelszó mentése</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="settings-card">
|
||||
<h3>Megosztott mappák</h3>
|
||||
{{if .SMBShares}}
|
||||
<div class="backup-table-wrap">
|
||||
<table class="db-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Név</th>
|
||||
<th>Mappa</th>
|
||||
<th>Írásvédett</th>
|
||||
<th>Felhőmentés</th>
|
||||
<th>Törlés</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{range .SMBShares}}
|
||||
<tr>
|
||||
<td class="mono">{{.Name}}</td>
|
||||
<td class="mono">{{.Path}}
|
||||
{{if not .Available}}<span class="badge badge-warn">A meghajtó nem elérhető.</span>{{end}}
|
||||
</td>
|
||||
<td>{{if .ReadOnly}}<span class="badge badge-neutral">igen</span>{{else}}nem{{end}}</td>
|
||||
<td>
|
||||
<form method="POST" action="/sharing/shares/offsite">
|
||||
{{$.CSRFField}}
|
||||
<input type="hidden" name="name" value="{{.Name}}">
|
||||
<input type="hidden" name="offsite" value="{{if .Offsite}}off{{else}}on{{end}}">
|
||||
<button type="submit" class="btn btn-xs btn-outline">
|
||||
{{if .Offsite}}bekapcsolva{{else}}kikapcsolva{{end}}
|
||||
</button>
|
||||
</form>
|
||||
</td>
|
||||
<td>
|
||||
<form method="POST" action="/sharing/shares/delete">
|
||||
{{$.CSRFField}}
|
||||
<input type="hidden" name="name" value="{{.Name}}">
|
||||
<button type="submit" class="btn btn-xs btn-danger-outline">Törlés</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="form-hint">
|
||||
A megosztás törlésekor <strong>a mappa és a fájlok megmaradnak</strong> — csak a hálózati
|
||||
elérés szűnik meg.
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="backup-table-empty">Még nincs megosztott mappa.</div>
|
||||
{{end}}
|
||||
</div>
|
||||
|
||||
<div class="settings-card">
|
||||
<h3>Új megosztás</h3>
|
||||
<form method="POST" action="/sharing/shares" id="share-create-form">
|
||||
{{.CSRFField}}
|
||||
<div class="form-group">
|
||||
<label for="share_name">Megosztás neve <span class="required">*</span></label>
|
||||
<input type="text" id="share_name" name="name" class="form-control mono"
|
||||
maxlength="15" required autocomplete="off">
|
||||
<div class="form-hint">Legfeljebb 15 karakter: betű, szám, kötőjel, aláhúzás.</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>
|
||||
<input type="radio" name="mode" value="new" checked onclick="shareModeSwitch()">
|
||||
Új mappa a tárhelyen
|
||||
</label>
|
||||
<label>
|
||||
<input type="radio" name="mode" value="existing" onclick="shareModeSwitch()">
|
||||
Meglévő mappa kiválasztása
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="form-group" id="share-mode-new">
|
||||
<label for="storage_root">Tárhely</label>
|
||||
<select id="storage_root" name="storage_root" class="form-control">
|
||||
{{range .StorageRoots}}
|
||||
<option value="{{.Path}}">{{.Label}}</option>
|
||||
{{end}}
|
||||
</select>
|
||||
<div class="form-hint">A mappa a kiválasztott tárhely <span class="mono">shares/</span> könyvtárában jön létre.</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group" id="share-mode-existing" style="display:none">
|
||||
<label>Kiválasztott mappa</label>
|
||||
<input type="text" id="share_path" name="path" class="form-control mono" readonly>
|
||||
<div class="form-actions">
|
||||
<button type="button" class="btn btn-sm btn-outline" onclick="shareBrowseOpen()">Tallózás</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>
|
||||
<input type="checkbox" name="read_only">
|
||||
Írásvédett (csak olvasható)
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-actions">
|
||||
<button type="submit" class="btn btn-primary">Megosztás létrehozása</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="confirm-overlay" id="share-browse-overlay" style="display:none">
|
||||
<div class="confirm-box">
|
||||
<h3>Mappa kiválasztása</h3>
|
||||
<div class="mono" id="share-browse-path"></div>
|
||||
<div id="share-browse-list"></div>
|
||||
<div class="form-actions">
|
||||
<button type="button" class="btn btn-sm btn-primary" onclick="shareBrowsePick()">Ezt választom</button>
|
||||
<button type="button" class="btn btn-sm btn-outline" onclick="shareBrowseClose()">Mégse</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
var shareBrowseCurrent = "";
|
||||
function shareModeSwitch(){
|
||||
var mode = document.querySelector('input[name="mode"]:checked').value;
|
||||
document.getElementById('share-mode-new').style.display = (mode === 'new') ? '' : 'none';
|
||||
document.getElementById('share-mode-existing').style.display = (mode === 'existing') ? '' : 'none';
|
||||
}
|
||||
function shareBrowseOpen(){
|
||||
document.getElementById('share-browse-overlay').style.display = '';
|
||||
shareBrowseLoad('');
|
||||
}
|
||||
function shareBrowseClose(){
|
||||
document.getElementById('share-browse-overlay').style.display = 'none';
|
||||
}
|
||||
function shareBrowsePick(){
|
||||
if(shareBrowseCurrent){ document.getElementById('share_path').value = shareBrowseCurrent; }
|
||||
shareBrowseClose();
|
||||
}
|
||||
function shareBrowseLoad(p){
|
||||
fetch('/api/sharing/browse?path=' + encodeURIComponent(p))
|
||||
.then(function(r){ return r.json(); })
|
||||
.then(function(d){
|
||||
shareBrowseCurrent = d.path || "";
|
||||
document.getElementById('share-browse-path').textContent = d.error ? d.error : (d.path || 'Tárhelyek');
|
||||
var list = document.getElementById('share-browse-list');
|
||||
list.innerHTML = '';
|
||||
if(d.parent){
|
||||
var up = document.createElement('div');
|
||||
up.innerHTML = '<button type="button" class="btn btn-xs btn-outline">.. vissza</button>';
|
||||
up.firstChild.onclick = function(){ shareBrowseLoad(d.parent); };
|
||||
list.appendChild(up);
|
||||
}
|
||||
(d.entries || []).forEach(function(e){
|
||||
var row = document.createElement('div');
|
||||
row.innerHTML = '<button type="button" class="btn btn-xs btn-outline"></button>';
|
||||
row.firstChild.textContent = e.name;
|
||||
row.firstChild.onclick = function(){ shareBrowseLoad(e.path); };
|
||||
list.appendChild(row);
|
||||
});
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
{{template "layout_end" .}}
|
||||
{{end}}
|
||||
Reference in New Issue
Block a user