harden anti-spam and file serving; backup restore to disk; json body limit
- Add honeypot field to public submission form + server-side check - Serve shared files only in context of a valid share link (/api/share/:shareToken/files/:fileToken) - Switch backup restore upload to diskStorage (50MB) with temp-dir cleanup - Limit JSON body to 1mb - Document fixed audit items
This commit is contained in:
@@ -127,6 +127,8 @@ nav a:hover{color:var(--text)}
|
||||
<textarea id="descInput" placeholder="Опиши, что проходил, что делал на занятии..." required></textarea>
|
||||
</div>
|
||||
|
||||
<input type="text" name="website" id="hpWebsite" tabindex="-1" autocomplete="off" style="display:none" value="">
|
||||
|
||||
<button class="btn" type="submit" id="submitBtn">Отправить</button>
|
||||
</div>
|
||||
</form>
|
||||
@@ -314,6 +316,7 @@ form.addEventListener('submit', async (e) => {
|
||||
fd.append('student_name', studentName || document.getElementById('nameInput').value);
|
||||
fd.append('group_id', groupInput.value);
|
||||
fd.append('description', document.getElementById('descInput').value);
|
||||
fd.append('website', document.getElementById('hpWebsite').value);
|
||||
|
||||
try {
|
||||
const res = await fetch(`${API}/api/entries`, { method: 'POST', body: fd });
|
||||
|
||||
+5
-4
@@ -103,16 +103,17 @@ function fileIcon(t){
|
||||
const icons = {image:'🖼️',music:'🎵',presentation:'📊',document:'📄',other:'📁'};
|
||||
return icons[t]||icons.other;
|
||||
}
|
||||
function filesHTML(files, password){
|
||||
function filesHTML(files, password, shareToken){
|
||||
if(!files||!files.length)return '';
|
||||
const pw = password ? `?password=${encodeURIComponent(password)}` : '';
|
||||
return `<div class="files-block">
|
||||
<div class="files-label">Работы резидента</div>
|
||||
<div class="files">${files.map(f=>{
|
||||
const t = fileType(f.name);
|
||||
const url = `/api/share/${shareToken}/files/${f.token}${pw}`;
|
||||
return `<a class="ftype-${t}" title="${esc(f.name)}" ${isImageFile(f.name)
|
||||
?`href="#" onclick="showImg('/api/files/${f.token}${pw}');return false;"`
|
||||
:`href="/api/files/${f.token}${pw}" download`}>
|
||||
?`href="#" onclick="showImg('${url}');return false;"`
|
||||
:`href="${url}" download`}>
|
||||
<span class="ficon" alt="${esc(f.name)}">${fileIcon(t)}</span>
|
||||
</a>`;
|
||||
}).join('')}</div>
|
||||
@@ -226,7 +227,7 @@ function renderShare(data, password) {
|
||||
${!isAnonymized ? `<div class="name">${esc(e.student_name)}</div>` : ''}
|
||||
${!isAnonymized && e.group_name ? `<div><span class="group">${esc(e.group_name)}</span></div>` : ''}
|
||||
<div class="desc">${esc(e.description)}</div>
|
||||
${filesHTML(e.files, password)}
|
||||
${filesHTML(e.files, password, currentToken)}
|
||||
</div>
|
||||
<div class="foot">${new Date(e.created_at).toLocaleString('ru',{day:'2-digit',month:'2-digit',year:'numeric',hour:'2-digit',minute:'2-digit'})}</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user