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

This commit is contained in:
2026-08-04 13:22:46 +05:00
parent aad23d3a96
commit 00cf2ebd9a
4 changed files with 385 additions and 67 deletions

View File

@@ -12,63 +12,65 @@ function loadEquipmentGallery(data) {
});
}
function loadSiteData() {
const data = {};
function loadTable(table, key) {
return new Promise((resolve) => {
db.all(`SELECT * FROM ${table} ORDER BY sort_order`, (err, rows) => {
if (err) { data[key] = []; resolve(); return; }
if (table === 'equipment' || table === 'partners') {
rows = rows.filter(r => r.is_active == 1);
}
if (table === 'services' || table === 'work_schemes') {
rows = rows.filter(r => r.is_active == 1);
}
data[key] = rows;
resolve();
});
});
}
function loadSettings(table, key, transformFn) {
return new Promise((resolve) => {
db.all(`SELECT * FROM ${table} ORDER BY id`, (err, rows) => {
if (err) { data[key] = {}; resolve(); return; }
if (transformFn) {
data[key] = transformFn(rows);
} else {
const obj = {};
rows.forEach(r => { obj[r.key] = r.value; });
data[key] = obj;
}
resolve();
});
});
}
return Promise.all([
loadSettings('hero_settings', 'hero'),
loadTable('hero_stats', 'hero_stats'),
loadSettings('about_settings', 'about'),
loadTable('about_features', 'about_features'),
loadTable('services', 'services'),
loadTable('work_schemes', 'work_schemes'),
loadTable('process_steps', 'process_steps'),
loadTable('equipment', 'equipment'),
loadEquipmentGallery(data),
loadTable('geo_regions', 'geo_regions'),
loadSettings('geo_settings', 'geo'),
loadTable('licenses', 'licenses'),
loadTable('license_gallery', 'license_gallery'),
loadTable('public_activities', 'public_activities'),
loadTable('activity_gallery', 'activity_gallery'),
loadTable('partners', 'partners'),
loadSettings('settings', 'settings')
]).then(() => data);
}
function setupRoutes(app) {
app.get('/api/site-data', (req, res) => {
const data = {};
function loadTable(table, key) {
return new Promise((resolve) => {
db.all(`SELECT * FROM ${table} ORDER BY sort_order`, (err, rows) => {
if (err) { data[key] = []; resolve(); return; }
if (table === 'equipment' || table === 'partners') {
rows = rows.filter(r => r.is_active == 1);
}
if (table === 'services' || table === 'work_schemes') {
rows = rows.filter(r => r.is_active == 1);
}
data[key] = rows;
resolve();
});
});
}
function loadSettings(table, key, transformFn) {
return new Promise((resolve) => {
db.all(`SELECT * FROM ${table} ORDER BY id`, (err, rows) => {
if (err) { data[key] = {}; resolve(); return; }
if (transformFn) {
data[key] = transformFn(rows);
} else {
const obj = {};
rows.forEach(r => { obj[r.key] = r.value; });
data[key] = obj;
}
resolve();
});
});
}
Promise.all([
loadSettings('hero_settings', 'hero'),
loadTable('hero_stats', 'hero_stats'),
loadSettings('about_settings', 'about'),
loadTable('about_features', 'about_features'),
loadTable('services', 'services'),
loadTable('work_schemes', 'work_schemes'),
loadTable('process_steps', 'process_steps'),
loadTable('equipment', 'equipment'),
loadEquipmentGallery(data),
loadTable('geo_regions', 'geo_regions'),
loadSettings('geo_settings', 'geo'),
loadTable('licenses', 'licenses'),
loadTable('license_gallery', 'license_gallery'),
loadTable('public_activities', 'public_activities'),
loadTable('activity_gallery', 'activity_gallery'),
loadTable('partners', 'partners'),
loadSettings('settings', 'settings')
]).then(() => {
res.json(data);
});
loadSiteData().then(data => res.json(data));
});
app.post('/api/leads', (req, res) => {
@@ -83,4 +85,4 @@ function setupRoutes(app) {
});
}
module.exports = { init, setupRoutes };
module.exports = { init, setupRoutes, loadSiteData };

250
modules/ssrRenderer.js Normal file
View File

@@ -0,0 +1,250 @@
const yearsSince2011 = new Date().getFullYear() - 2011;
function esc(s) { return String(s||'').replace(/&/g,'&amp;').replace(/"/g,'&quot;').replace(/</g,'&lt;').replace(/>/g,'&gt;'); }
function imgSrc(path) {
if (!path) return './img/hero-bg.webp';
if (path.startsWith('./')) return path;
if (path.startsWith('http')) return path;
if (/^(equipment|partner|license|activity|hero)_images\//.test(path)) return '/data/' + path;
return './img/' + path;
}
function renderHero(data) {
var h = data.hero || {};
var stats = data.hero_stats || [];
var phone800 = h.phone_800 || '8 (800) 555-29-31';
var phoneEkb = h.phone_ekb || '8 (343) 237-25-66';
var email = h.email || 'sts_zakaz@mail.ru';
var address = h.address || 'г. Екатеринбург, ул. Московская, 11, оф. 702';
var contactsHtml = '';
if (phone800 && phoneEkb) {
contactsHtml += '<div class="hero-contact-item"><svg viewBox="0 0 24 24"><path d="M6.62 10.79a15.053 15.053 0 006.59 6.59l2.2-2.2a1 1 0 011.01-.24c1.12.37 2.33.57 3.58.57a1 1 0 011 1V20a1 1 0 01-1 1A17 17 0 013 4a1 1 0 011-1h3.5a1 1 0 011 1c0 1.25.2 2.46.57 3.58a1 1 0 01-.24 1.01l-2.2 2.2z"/></svg><div><a href="tel:' + phone800.replace(/\D/g,'') + '">' + phone800 + '</a><br><a href="tel:' + phoneEkb.replace(/\D/g,'') + '">' + phoneEkb + '</a></div></div>';
}
contactsHtml += '<div class="hero-contact-item"><svg viewBox="0 0 24 24"><path d="M20 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 4l-8 5-8-5V6l8 5 8-5v2z"/></svg><a href="mailto:' + email + '">' + email + '</a></div>';
contactsHtml += '<div class="hero-contact-item"><svg viewBox="0 0 24 24"><path d="M12 2C8.13 2 5 5.13 5 9c0 5.25 7 13 7 13s7-7.75 7-13c0-3.87-3.13-7-7-7zm0 9.5A2.5 2.5 0 1112 6.5a2.5 2.5 0 010 5z"/></svg><span>' + address + '</span></div>';
var statsHtml = '';
stats.forEach(function(s) {
var val = s.is_dynamic == 1 ? yearsSince2011 : s.value;
statsHtml += '<div class="hero-stat"><div class="hero-stat-number">' + val + '</div><div class="hero-stat-label">' + s.label + '</div></div>';
});
var badgeHtml = h.badge_text ? '<div class="hero-badge"><svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor"><path d="M12 2L15.09 8.26L22 9.27L17 14.14L18.18 21.02L12 17.77L5.82 21.02L7 14.14L2 9.27L8.91 8.26L12 2Z"/></svg> ' + h.badge_text + '</div>' : '';
var h1text = (h.h1_text || 'демонтаж нефтегазопроводов по России, утилизация отходов').replace('{years}', yearsSince2011);
return badgeHtml +
'<h1>' + h1text + '</h1>' +
'<p class="hero-subtitle">' + (h.subtitle || '').replace('{years}', yearsSince2011) + '</p>' +
'<div class="hero-contacts">' + contactsHtml + '</div>' +
'<div class="hero-buttons">' +
'<a href="' + (h.button1_link || '#contacts') + '" class="btn btn-primary">' + (h.button1_text || 'Оставить заявку') + '</a>' +
'<a href="' + (h.button2_link || '#services') + '" class="btn btn-outline">' + (h.button2_text || 'Наши услуги') + '</a>' +
'</div>' +
'<div class="hero-stats">' + statsHtml + '</div>';
}
function renderAbout(data) {
var a = data.about || {};
var features = data.about_features || [];
var featsHtml = features.map(function(f) {
return '<div class="about-feature"><div class="about-feature-icon"><svg viewBox="0 0 24 24">' + (f.svg_icon || '') + '</svg></div><div class="about-feature-text"><strong>' + f.title + '</strong>' + (f.description || '') + '</div></div>';
}).join('');
return '<div class="section-header"><h2 class="section-title">О <span>компании</span></h2><p class="section-subtitle">' + (a.section_subtitle || '') + '</p></div>' +
'<div class="about-grid">' +
'<div class="about-image"><img src="' + imgSrc(a.about_image) + '" alt="" loading="lazy"></div>' +
'<div class="about-text"><h3>' + (a.main_heading || '') + '</h3>' +
(a.paragraph_1 ? '<p>' + a.paragraph_1 + '</p>' : '') +
(a.paragraph_2 ? '<p>' + a.paragraph_2 + '</p>' : '') +
(a.paragraph_3 ? '<p>' + a.paragraph_3 + '</p>' : '') +
'<div class="about-features">' + featsHtml + '</div></div></div>';
}
function renderServices(data) {
var services = data.services || [];
var st = data.settings || {};
var cardsHtml = services.map(function(s) {
var items = [];
try { items = JSON.parse(s.items || '[]'); } catch(e) {}
return '<div class="service-card' + (s.is_large == 1 ? ' large' : '') + '">' +
'<div class="service-icon"><svg viewBox="0 0 24 24">' + (s.svg_icon || '') + '</svg></div>' +
'<h3>' + s.title + '</h3><p>' + (s.description || '') + '</p>' +
(items.length ? '<ul class="service-list">' + items.map(function(i) { return '<li>' + i + '</li>'; }).join('') + '</ul>' : '') +
'</div>';
}).join('');
return '<div class="section-header center"><h2 class="section-title">Наши <span>услуги</span></h2><p class="section-subtitle">' + (st.section_services_subtitle || '') + '</p></div>' +
'<div class="services-grid">' + cardsHtml + '</div>';
}
function renderSchemes(data) {
var schemes = data.work_schemes || [];
var st = data.settings || {};
var cardsHtml = schemes.map(function(s) {
var items = [];
try { items = JSON.parse(s.items || '[]'); } catch(e) {}
return '<div class="service-card">' +
'<div class="service-icon"><svg viewBox="0 0 24 24">' + (s.svg_icon || '') + '</svg></div>' +
'<h3>' + s.title + '</h3><p>' + (s.description || '') + '</p>' +
(items.length ? '<ul class="service-list">' + items.map(function(i) { return '<li>' + i + '</li>'; }).join('') + '</ul>' : '') +
'</div>';
}).join('');
return '<div class="section-header center"><h2 class="section-title">Гибкие <span>схемы работы</span></h2><p class="section-subtitle">' + (st.section_schemes_subtitle || '') + '</p></div>' +
'<div class="services-grid">' + cardsHtml + '</div>';
}
function renderProcess(data) {
var steps = data.process_steps || [];
var st = data.settings || {};
var stepsHtml = steps.map(function(s) {
return '<div class="process-step"><div class="process-number">' + (s.step_number || s.sort_order + 1) + '</div><div class="process-content"><h3>' + s.title + '</h3><p>' + (s.description || '') + '</p></div></div>';
}).join('');
return '<div class="section-header center"><h2 class="section-title">Этапы <span>работы</span></h2><p class="section-subtitle">' + (st.section_process_subtitle || '') + '</p></div>' +
'<div class="process-timeline">' + stepsHtml + '</div>';
}
function renderEquipment(data) {
var eq = data.equipment || [];
var gallery = data.equipment_gallery || [];
var st = data.settings || {};
function getImages(e) {
var imgs = [];
if (gallery.length) {
gallery.filter(function(g) { return g.equipment_id === e.id; }).forEach(function(g) {
if (g.image_path) imgs.push(g.image_path);
});
}
if (imgs.length === 0 && e.image_path) imgs.push(e.image_path);
return imgs;
}
var cardsHtml = eq.map(function(e) {
var specs = [];
try { specs = JSON.parse(e.specs || '[]'); } catch(e2) {}
var imgs = getImages(e);
var imgHtml = imgs.length > 0
? '<img src="' + imgSrc(imgs[0]) + '" loading="lazy" alt="' + esc(e.name) + '" style="width:100%;height:100%;object-fit:cover">'
: '<div style="width:100%;height:100%;background:var(--bg-alt);display:flex;align-items:center;justify-content:center;color:var(--text-light)">Нет фото</div>';
return '<div class="equipment-card"><div class="equipment-card-image">' +
imgHtml +
'<div class="equip-overlay"><span class="brand">' + esc(e.brand || '') + '</span></div>' +
'</div><div class="equipment-card-body"><h3>' + esc(e.name) + '</h3>' +
(e.qty ? '<div class="equipment-qty">' + esc(e.qty) + '</div>' : '') +
(specs.length ? '<div class="equipment-specs">' + specs.map(function(sp) { return '<div class="equipment-spec"><span class="equipment-spec-label">' + esc(sp.label) + '</span><span class="equipment-spec-value">' + esc(sp.value) + '</span></div>'; }).join('') + '</div>' : '') +
'</div></div>';
}).join('');
return '<div class="section-header center"><h2 class="section-title">Наша <span>спецтехника</span></h2><p class="section-subtitle">' + esc(st.section_equipment_subtitle || '') + '</p></div>' +
'<div class="equipment-grid">' + cardsHtml + '</div>';
}
function renderGeography(data) {
var geo = data.geo || {};
var regions = data.geo_regions || [];
var st = data.settings || {};
var regionsHtml = regions.map(function(r) {
return '<div class="geo-region"><svg viewBox="0 0 24 24"><path d="M12 2C8.13 2 5 5.13 5 9c0 5.25 7 13 7 13s7-7.75 7-13c0-3.87-3.13-7-7-7zm0 9.5A2.5 2.5 0 1112 6.5a2.5 2.5 0 010 5z"/></svg><span>' + (r.name || '') + '</span></div>';
}).join('');
return '<div class="section-header center"><h2 class="section-title">География <span>присутствия</span></h2><p class="section-subtitle">' + (geo.section_subtitle || st.section_services_subtitle || '') + '</p></div>' +
'<div class="geo-content"><div><div class="geo-regions">' + regionsHtml + '</div>' +
(geo.difficult_title ? '<div class="geo-hard"><h4>' + geo.difficult_title + '</h4><p>' + (geo.difficult_desc || '') + '</p></div>' : '') +
'</div><div><div class="geo-map"><img src="' + imgSrc(geo.map_image) + '" loading="lazy" alt=""></div></div></div>';
}
function renderLicenses(data) {
var licenses = data.licenses || [];
var gallery = data.license_gallery || [];
var st = data.settings || {};
var cardsHtml = licenses.map(function(l) {
return '<div class="license-card"><div class="license-icon"><svg viewBox="0 0 24 24">' + (l.svg_icon || '') + '</svg></div><div><h4>' + l.title + '</h4><p>' + (l.description || '') + '</p></div></div>';
}).join('');
var galleryHtml = gallery.map(function(g) {
return '<div class="license-gallery-item" data-src="' + imgSrc(g.image_path) + '" data-title="' + esc(g.title) + '" data-desc="' + esc(g.description || '') + '">' +
'<div class="license-gallery-thumb"><img src="' + imgSrc(g.image_path) + '" loading="lazy" alt="' + esc(g.title) + '"><div class="license-gallery-zoom"><svg viewBox="0 0 24 24"><path d="M15.5 14h-.79l-.28-.27A6.471 6.471 0 0016 9.5 6.5 6.5 0 109.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/></svg></div></div>' +
'<div class="license-gallery-body"><h4>' + g.title + '</h4><p>' + (g.description || '') + '</p></div></div>';
}).join('');
return '<div class="section-header center"><h2 class="section-title">Лицензии и <span>разрешения</span></h2><p class="section-subtitle">' + (st.section_licenses_subtitle || '') + '</p></div>' +
'<div class="licenses-grid">' + cardsHtml + '</div>' +
(gallery.length ? '<div class="license-gallery">' + galleryHtml + '</div>' : '');
}
function renderActivity(data) {
var acts = data.public_activities || [];
var gallery = data.activity_gallery || [];
var st = data.settings || {};
var cardsHtml = acts.map(function(a) {
return '<div class="activity-card"><div class="activity-card-icon"><svg viewBox="0 0 24 24">' + (a.svg_icon || '') + '</svg></div><h4>' + a.title + '</h4><p>' + (a.description || '') + '</p></div>';
}).join('');
var galleryHtml = gallery.length ? '<div class="activity-gallery"><div class="activity-gallery-title">Благодарственные письма</div>' +
gallery.map(function(g) {
return '<div class="license-gallery-item" data-src="' + imgSrc(g.image_path) + '" data-title="' + esc(g.title) + '" data-desc="' + esc(g.description || '') + '">' +
'<div class="license-gallery-thumb"><img src="' + imgSrc(g.image_path) + '" loading="lazy" alt="' + esc(g.title) + '"><div class="license-gallery-zoom"><svg viewBox="0 0 24 24"><path d="M15.5 14h-.79l-.28-.27A6.471 6.471 0 0016 9.5 6.5 6.5 0 109.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/></svg></div></div>' +
'<div class="license-gallery-body"><h4>' + g.title + '</h4><p>' + (g.description || '') + '</p></div></div>';
}).join('') + '</div>' : '';
return '<div class="section-header center"><h2 class="section-title">Общественная <span>деятельность</span></h2><p class="section-subtitle">' + (st.section_activity_subtitle || '') + '</p></div>' +
'<div class="activity-cards">' + cardsHtml + '</div>' + galleryHtml;
}
function renderPartners(data) {
var partners = data.partners || [];
var st = data.settings || {};
var duplicated = partners.concat(partners);
var trackHtml = duplicated.map(function(p) {
return '<img src="' + imgSrc(p.logo_path) + '" loading="lazy" alt="' + esc(p.name || '') + '">';
}).join('');
return '<div class="section-header center"><h2 class="section-title">Наши <span>партнёры</span></h2><p class="section-subtitle">' + (st.section_partners_subtitle || '') + '</p></div>' +
'<div class="partners-scroll"><div class="partners-track">' + trackHtml + '</div></div>';
}
function renderContacts(data) {
var s = data.settings || {};
var h = data.hero || {};
var phone800 = h.phone_800 || s.contact_phone_800 || '8 (800) 555-29-31';
var phoneEkb = h.phone_ekb || s.contact_phone_ekb || '8 (343) 237-25-66';
var email = h.email || s.contact_email || 'sts_zakaz@mail.ru';
var address = h.address || s.contact_address || 'г. Екатеринбург, ул. Московская, 11, оф. 702';
var workHours = (s.contact_work_hours || 'Пн–Пт: 9:00 18:00|Сб: 10:00 15:00').replace(/\|/g, '<br>');
var mapImg = s.contact_map_image || './img/secret.webp';
return '<div class="section-header center"><h2 class="section-title">Свяжитесь <span>с нами</span></h2><p class="section-subtitle">' + (s.section_contacts_subtitle || '') + '</p></div>' +
'<div class="contacts-grid"><div>' +
'<div class="contact-info-list">' +
'<div class="contact-info-item"><div class="contact-info-icon"><svg viewBox="0 0 24 24"><path d="M6.62 10.79a15.053 15.053 0 006.59 6.59l2.2-2.2a1 1 0 011.01-.24c1.12.37 2.33.57 3.58.57a1 1 0 011 1V20a1 1 0 01-1 1A17 17 0 013 4a1 1 0 011-1h3.5a1 1 0 011 1c0 1.25.2 2.46.57 3.58a1 1 0 01-.24 1.01l-2.2 2.2z"/></svg></div><div><h4>Телефон</h4><a href="tel:' + phone800.replace(/\D/g,'') + '">' + phone800 + '</a><br><a href="tel:' + phoneEkb.replace(/\D/g,'') + '">' + phoneEkb + '</a></div></div>' +
'<div class="contact-info-item"><div class="contact-info-icon"><svg viewBox="0 0 24 24"><path d="M20 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 4l-8 5-8-5V6l8 5 8-5v2z"/></svg></div><div><h4>Email</h4><a href="mailto:' + email + '">' + email + '</a></div></div>' +
'<div class="contact-info-item"><div class="contact-info-icon"><svg viewBox="0 0 24 24"><path d="M12 2C8.13 2 5 5.13 5 9c0 5.25 7 13 7 13s7-7.75 7-13c0-3.87-3.13-7-7-7zm0 9.5A2.5 2.5 0 1112 6.5a2.5 2.5 0 010 5z"/></svg></div><div><h4>Адрес</h4><p>' + address + '</p></div></div>' +
'<div class="contact-info-item"><div class="contact-info-icon"><svg viewBox="0 0 24 24"><path d="M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8zm.5-13H11v6l5.25 3.15.75-1.23-4.5-2.67z"/></svg></div><div><h4>Режим работы</h4><p>' + workHours + '</p></div></div>' +
'</div><div class="contact-map"><img src="' + imgSrc(mapImg) + '" loading="lazy" alt=""></div>' +
'</div><div>' +
'<div class="contact-form" id="contactForm"><h3>' + (s.form_title || 'Оставить заявку') + '</h3>' +
'<p class="form-subtitle">' + (s.form_subtitle || 'Заполните форму и мы свяжемся с вами') + '</p>' +
'<form id="feedbackForm" onsubmit="return handleFormSubmit(event)">' +
'<div class="form-group"><label for="name">Ваше имя *</label><input type="text" id="name" name="name" placeholder="Иван Иванов" required></div>' +
'<div class="form-group"><label for="phone">Телефон *</label><input type="tel" id="phone" name="phone" placeholder="+7 (___) ___-__-__" required></div>' +
'<div class="form-group"><label for="message">Сообщение</label><textarea id="message" name="message" placeholder="Опишите ваш запрос..."></textarea></div>' +
'<div class="form-group"><label class="form-consent-label"><input type="checkbox" name="consent" required id="consent"><span>Я согласен на <a href="/privacy.html" target="_blank">обработку персональных данных</a></span></label></div>' +
'<button type="submit" class="btn btn-primary">Отправить заявку</button>' +
'</form>' +
'<div class="form-success" id="formSuccess"><svg viewBox="0 0 24 24"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"/></svg><h4>Заявка отправлена!</h4><p>Мы свяжемся с вами в ближайшее время</p></div>' +
'</div></div></div>';
}
function renderAll(data) {
return {
heroContent: renderHero(data),
aboutContainer: renderAbout(data),
servicesContainer: renderServices(data),
schemesContainer: renderSchemes(data),
processContainer: renderProcess(data),
equipmentContainer: renderEquipment(data),
geographyContainer: renderGeography(data),
licensesContainer: renderLicenses(data),
activityContainer: renderActivity(data),
contactsContainer: renderContacts(data),
partnersContainer: renderPartners(data)
};
}
module.exports = { renderAll };

View File

@@ -3,13 +3,55 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
<title>СТС-Авто — Демонтаж нефтегазопроводов по России, утилизация отходов</title>
<meta name="description" content="Демонтаж нефтегазопроводов по России, утилизация отходов. Собственная спецтехника. Лицензии СРО и НОСТРОЙ. Более {years} лет опыта.">
<title>Демонтаж нефтегазопроводов, утилизация отходов, рекультивация земель — СТС-Авто</title>
<meta name="description" content="Более 15 лет опыта в демонтаже трубопроводов, утилизации промышленных отходов и рекультивации земель по всей России. Работаем с объектами любого масштаба.">
<link rel="canonical" href="https://sts-avto.com/">
<meta property="og:title" content="Демонтаж и утилизация — СТС-Авто">
<meta property="og:description" content="Профессиональный демонтаж нефтегазопроводов и рекультивация земель.">
<meta property="og:image" content="https://sts-avto.com/img/og-image.webp">
<meta property="og:url" content="https://sts-avto.com/">
<meta property="og:type" content="website">
<meta property="og:locale" content="ru_RU">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="Демонтаж и утилизация — СТС-Авто">
<meta name="twitter:description" content="Профессиональный демонтаж нефтегазопроводов и рекультивация земель.">
<meta name="twitter:image" content="https://sts-avto.com/img/og-image.webp">
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Organization",
"name": "СТС-Авто",
"url": "https://sts-avto.com/",
"logo": "https://sts-avto.com/img/logo.webp",
"address": {
"@type": "PostalAddress",
"addressLocality": "Екатеринбург",
"streetAddress": "ул. Московская, 11, оф. 702"
},
"telephone": "+7-800-555-29-31",
"areaServed": {
"@type": "Country",
"name": "Россия"
}
}
</script>
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "BreadcrumbList",
"itemListElement": [
{ "@type": "ListItem", "position": 1, "name": "Главная", "item": "https://sts-avto.com/" },
{ "@type": "ListItem", "position": 2, "name": "Услуги", "item": "https://sts-avto.com/#services" }
]
}
</script>
<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 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 rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" media="print" onload="this.media='all'">
<noscript><link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css"></noscript>
<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" media="print" onload="this.media='all'">
<noscript><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"></noscript>
<style>
*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
@@ -701,7 +743,10 @@ img { max-width: 100%; display: block; }
<section class="hero" id="hero">
<div class="hero-bg"><img src="./img/hero-bg.webp" alt="" fetchpriority="high"></div>
<div class="container"><div class="hero-content" id="heroContent"></div></div>
<div class="container">
<h1 style="position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0">Демонтаж трубопроводов и утилизация отходов по всей России</h1>
<div class="hero-content" id="heroContent"></div>
</div>
</section>
<section class="section section-alt" id="about">
@@ -783,7 +828,7 @@ img { max-width: 100%; display: block; }
<svg viewBox="0 0 24 24"><path d="M7.41 15.41L12 10.83l4.59 4.58L18 14l-6-6-6 6z"/></svg>
</button>
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js" defer></script>
<script>
(function() {
'use strict';

View File

@@ -46,7 +46,16 @@ async function convertImagesToWebp() {
}
app.use(express.json());
app.use(express.static(path.join(__dirname, 'public')));
app.get('/robots.txt', (req, res) => {
res.sendFile(path.join(__dirname, 'public', 'robots.txt'));
});
app.get('/sitemap.xml', (req, res) => {
res.sendFile(path.join(__dirname, 'public', 'sitemap.xml'));
});
app.use(express.static(path.join(__dirname, 'public'), { index: false }));
const dataDir = path.join(__dirname, 'data');
if (!fs.existsSync(dataDir)) fs.mkdirSync(dataDir);
@@ -80,6 +89,7 @@ const settingsModule = require('./modules/settings');
const usersModule = require('./modules/users');
const leadsModule = require('./modules/leads');
const siteDataModule = require('./modules/siteData');
const ssrRenderer = require('./modules/ssrRenderer');
[
crudModule, authModule, heroModule, aboutModule,
@@ -114,11 +124,22 @@ app.get('/admin', (req, res) => {
res.sendFile(path.join(__dirname, 'public', 'admin.html'));
});
app.get('/', (req, res) => {
const years = new Date().getFullYear() - 2011;
app.get('/', async (req, res) => {
const htmlPath = path.join(__dirname, 'public', 'index.html');
const html = fs.readFileSync(htmlPath, 'utf8');
res.send(html.replace(/\{years\}/g, String(years)));
let html = fs.readFileSync(htmlPath, 'utf8');
try {
const siteData = await siteDataModule.loadSiteData();
const ssrHtml = ssrRenderer.renderAll(siteData);
for (const [containerId, content] of Object.entries(ssrHtml)) {
const placeholder = 'id="' + containerId + '">';
html = html.replace(placeholder, placeholder + content);
}
} catch (err) {
console.error('SSR render error:', err.message);
}
res.send(html);
});
convertImagesToWebp().then(() => console.log('Images ready'));