фикс край
All checks were successful
Deploy sts / deploy-sber (push) Successful in 1m12s

This commit is contained in:
2026-08-03 23:14:14 +05:00
parent c4662c2a63
commit 0b61d5be82
11 changed files with 133 additions and 1 deletions

View File

@@ -143,7 +143,11 @@ function initDatabase() {
)`); )`);
db.get(`SELECT COUNT(*) as cnt FROM settings`, (err, row) => { db.get(`SELECT COUNT(*) as cnt FROM settings`, (err, row) => {
if (row && row.cnt > 0) { resolve(); return; } if (row && row.cnt > 0) {
db.run(`INSERT OR IGNORE INTO settings (key, value) VALUES ('logo_path', './img/logo.webp')`);
resolve();
return;
}
seedAll().then(resolve).catch(reject); seedAll().then(resolve).catch(reject);
}); });
}); });
@@ -274,6 +278,7 @@ async function seedAll() {
]); ]);
await runEach('INSERT OR IGNORE INTO settings (key, value) VALUES (?, ?)', [ await runEach('INSERT OR IGNORE INTO settings (key, value) VALUES (?, ?)', [
['logo_path', './img/logo.webp'],
['contact_phone_800', '8 (800) 555-29-31'], ['contact_phone_ekb', '8 (343) 237-25-66'], ['contact_phone_800', '8 (800) 555-29-31'], ['contact_phone_ekb', '8 (343) 237-25-66'],
['contact_email', 'sts_zakaz@mail.ru'], ['contact_address', 'г. Екатеринбург, ул. Московская, 11, оф. 702'], ['contact_email', 'sts_zakaz@mail.ru'], ['contact_address', 'г. Екатеринбург, ул. Московская, 11, оф. 702'],
['contact_work_hours', 'Пн–Пт: 9:00 18:00|Сб: 10:00 15:00'], ['contact_map_image', './img/secret.webp'], ['contact_work_hours', 'Пн–Пт: 9:00 18:00|Сб: 10:00 15:00'], ['contact_map_image', './img/secret.webp'],

View File

@@ -1,3 +1,8 @@
const multer = require('multer');
const path = require('path');
const fs = require('fs');
const sharp = require('sharp');
let db; let db;
function init(database) { db = database; } function init(database) { db = database; }
@@ -5,6 +10,60 @@ function init(database) { db = database; }
function setupRoutes(app, auth) { function setupRoutes(app, auth) {
const { authenticateToken, requireAdmin } = auth; const { authenticateToken, requireAdmin } = auth;
const imgDir = path.join(__dirname, '..', '..', 'public', 'img');
const favDir = path.join(__dirname, '..', '..', 'public');
if (!fs.existsSync(imgDir)) fs.mkdirSync(imgDir, { recursive: true });
const storage = multer.memoryStorage();
const uploadLogo = multer({
storage,
limits: { fileSize: 5 * 1024 * 1024 },
fileFilter: (req, file, cb) => {
const allowed = ['.jpg', '.jpeg', '.png', '.webp', '.svg', '.gif', '.bmp'];
const ext = path.extname(file.originalname).toLowerCase();
if (!allowed.includes(ext)) return cb(new Error('Недопустимый формат файла'));
cb(null, true);
}
});
app.post('/api/admin/settings/logo', authenticateToken, requireAdmin, uploadLogo.single('logo'), async (req, res) => {
if (!req.file) return res.status(400).json({ error: 'Файл не загружен' });
try {
const ext = path.extname(req.file.originalname).toLowerCase();
const logoWebp = path.join(imgDir, 'logo.webp');
const fav32 = path.join(favDir, 'favicon-32x32.png');
const fav16 = path.join(favDir, 'favicon-16x16.png');
const apple = path.join(favDir, 'apple-touch-icon.png');
const pipeline = sharp(req.file.buffer);
if (ext !== '.svg') {
await pipeline.clone().webp({ quality: 85 }).toFile(logoWebp);
} else {
fs.writeFileSync(logoWebp, req.file.buffer);
}
if (ext !== '.svg') {
await pipeline.clone().resize(32, 32, { fit: 'contain', background: { r: 0, g: 0, b: 0, alpha: 0 } }).png().toFile(fav32);
await pipeline.clone().resize(16, 16, { fit: 'contain', background: { r: 0, g: 0, b: 0, alpha: 0 } }).png().toFile(fav16);
await pipeline.clone().resize(180, 180, { fit: 'contain', background: { r: 0, g: 0, b: 0, alpha: 0 } }).png().toFile(apple);
} else {
fs.writeFileSync(fav32, req.file.buffer);
fs.writeFileSync(fav16, req.file.buffer);
fs.writeFileSync(apple, req.file.buffer);
}
const logoPath = './img/logo.webp';
db.run(`INSERT INTO settings (key, value) VALUES ('logo_path', ?) ON CONFLICT(key) DO UPDATE SET value = ?`, [logoPath, logoPath]);
res.json({ path: logoPath, url: logoPath });
} catch (e) {
console.error('Ошибка при обработке логотипа:', e);
res.status(500).json({ error: 'Ошибка обработки изображения' });
}
});
app.get('/api/admin/settings', authenticateToken, requireAdmin, (req, res) => { app.get('/api/admin/settings', authenticateToken, requireAdmin, (req, res) => {
db.all(`SELECT * FROM settings ORDER BY id`, (err, rows) => { db.all(`SELECT * FROM settings ORDER BY id`, (err, rows) => {
if (err) return res.status(500).json({ error: 'Ошибка БД' }); if (err) return res.status(500).json({ error: 'Ошибка БД' });

View File

@@ -813,6 +813,14 @@ window.tab_settings = function(tabId) {
document.getElementById('mainContent').innerHTML = h() + document.getElementById('mainContent').innerHTML = h() +
'<div class="settings-grid">' + '<div class="settings-grid">' +
'<div class="settings-card"><h3>Логотип сайта</h3>' +
'<div id="logoPreview" style="margin-bottom:10px">' +
(s.logo_path ? '<img src="' + s.logo_path + '" class="preview-img" style="max-height:80px">' : '<span class="text-muted">Логотип не загружен</span>') +
'</div>' +
'<input type="file" id="logoFileInput" accept=".jpg,.jpeg,.png,.webp,.svg,.gif,.bmp" style="margin-bottom:8px;width:100%">' +
'<button class="btn btn-sm btn-primary" onclick="winUploadLogo()"><i class="fas fa-upload"></i> Загрузить логотип</button>' +
'<p style="font-size:.75rem;color:var(--text-gray);margin-top:3px">При загрузке автоматически создаются favicon и apple-touch-icon</p>' +
'</div>' +
'<div class="settings-card"><h3>Контакты</h3>' + '<div class="settings-card"><h3>Контакты</h3>' +
contactKeys.map(function(k) { return '<p style="margin-bottom:4px"><strong>' + k + ':</strong> <span class="text-muted">' + esc(String(s[k]||'').substring(0,80)) + '</span></p>'; }).join('') + contactKeys.map(function(k) { return '<p style="margin-bottom:4px"><strong>' + k + ':</strong> <span class="text-muted">' + esc(String(s[k]||'').substring(0,80)) + '</span></p>'; }).join('') +
'<button class="btn btn-sm btn-primary" style="margin-top:8px" onclick="winEditSettings(\'contacts\')"><i class="fas fa-edit"></i> Редактировать</button>' + '<button class="btn btn-sm btn-primary" style="margin-top:8px" onclick="winEditSettings(\'contacts\')"><i class="fas fa-edit"></i> Редактировать</button>' +
@@ -824,6 +832,25 @@ window.tab_settings = function(tabId) {
'</div>'; '</div>';
window._settings = s; window._settings = s;
window.winUploadLogo = async function() {
var inp = document.getElementById('logoFileInput');
if (!inp.files.length) { toast('Выберите файл','error'); return; }
var fd = new FormData();
fd.append('logo', inp.files[0]);
try {
var r = await fetch(BASE + '/settings/logo', {
method: 'POST', headers: {'Authorization':'Bearer ' + token}, body: fd
}).then(function(x) { return x.json(); });
if (r.path) {
toast('Логотип обновлён! Favicon сгенерированы');
loadCurrentTab();
} else {
toast(r.error || 'Ошибка загрузки','error');
}
} catch(e) {
toast('Ошибка загрузки','error');
}
};
window.winEditSettings = function(mode) { window.winEditSettings = function(mode) {
var s = window._settings; var s = window._settings;
var keys = mode === 'contacts' ? contactKeys : formKeys; var keys = mode === 'contacts' ? contactKeys : formKeys;

BIN
public/apple-touch-icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

BIN
public/favicon-16x16.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 468 B

BIN
public/favicon-32x32.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 136 KiB

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 73 KiB

After

Width:  |  Height:  |  Size: 136 KiB

View File

@@ -5,6 +5,9 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover"> <meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
<title>СТС-Авто — Демонтаж нефтегазопроводов по России, утилизация отходов</title> <title>СТС-Авто — Демонтаж нефтегазопроводов по России, утилизация отходов</title>
<meta name="description" content="Демонтаж нефтегазопроводов по России, утилизация отходов. Собственная спецтехника. Лицензии СРО и НОСТРОЙ. Более {years} лет опыта."> <meta name="description" content="Демонтаж нефтегазопроводов по России, утилизация отходов. Собственная спецтехника. Лицензии СРО и НОСТРОЙ. Более {years} лет опыта.">
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
<link rel="apple-touch-icon" href="/apple-touch-icon.png">
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css"> <link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css">
<link href="https://fonts.googleapis.com/css2?family=Montserrat:wght@400;500;600;700;800;900&family=Roboto+Condensed:wght@400;500;700&display=swap" rel="stylesheet"> <link href="https://fonts.googleapis.com/css2?family=Montserrat:wght@400;500;600;700;800;900&family=Roboto+Condensed:wght@400;500;700&display=swap" rel="stylesheet">
<style> <style>
@@ -1130,6 +1133,10 @@ img { max-width: 100%; display: block; }
function initSiteData(data) { function initSiteData(data) {
SITE = data; SITE = data;
if (data.settings && data.settings.logo_path) {
var logoImg = document.querySelector('.logo img');
if (logoImg) logoImg.src = data.settings.logo_path;
}
renderHero(data); renderHero(data);
renderAbout(data); renderAbout(data);
renderServices(data); renderServices(data);

6
public/robots.txt Normal file
View File

@@ -0,0 +1,6 @@
User-agent: *
Allow: /
Disallow: /admin
Disallow: /api/
Sitemap: https://sts-avto.com/sitemap.xml

28
public/sitemap.xml Normal file
View File

@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>https://sts-avto.com/</loc>
<changefreq>weekly</changefreq>
<priority>1.0</priority>
</url>
<url>
<loc>https://sts-avto.com/privacy.html</loc>
<changefreq>monthly</changefreq>
<priority>0.3</priority>
</url>
<url>
<loc>https://sts-avto.com/terms.html</loc>
<changefreq>monthly</changefreq>
<priority>0.3</priority>
</url>
<url>
<loc>https://sts-avto.com/cookies.html</loc>
<changefreq>monthly</changefreq>
<priority>0.3</priority>
</url>
<url>
<loc>https://sts-avto.com/requisites.html</loc>
<changefreq>monthly</changefreq>
<priority>0.3</priority>
</url>
</urlset>