feat: harden content workflows and staging smoke tests
CI — Test & Build / 🧪 Run Tests & Generate Reports (push) Waiting to run
CI — Test & Build / 🏗️ Build Docker Image (push) Blocked by required conditions
CI — Test & Build / 🐳 Docker integration & API E2E (push) Blocked by required conditions
CI — Test & Build / 🌐 Staging Playwright smoke (push) Waiting to run

This commit is contained in:
Do Siki
2026-08-17 16:08:57 +02:00
parent 35e341d5b2
commit 0047e1b441
28 changed files with 1208 additions and 360 deletions
+56
View File
@@ -0,0 +1,56 @@
import { expect, test } from '@playwright/test'
test('SMOKE-01: health endpoint is available', async ({ request }) => {
const response = await request.get('/api/health')
expect(response.status()).toBe(200)
await expect(response).toBeOK()
await expect(response.json()).resolves.toMatchObject({ status: 'ok' })
})
test('SMOKE-02: homepage renders its critical shell without console errors', async ({ page }) => {
const consoleErrors: string[] = []
page.on('console', message => {
if (message.type() === 'error') consoleErrors.push(message.text())
})
await page.goto('/')
await expect(page.getByRole('heading', { level: 1 })).toBeVisible()
await expect(page.getByRole('navigation')).toBeVisible()
await expect(page.getByRole('link', { name: /beszéljük át/i })).toBeVisible()
await expect(page.locator('footer')).toBeVisible()
expect(consoleErrors).toEqual([])
})
test('SMOKE-03: main navigation opens every public core page', async ({ page }) => {
const targets = [
['Kezdőlap', '/'],
['Rólunk', '/rolunk'],
['Szolgáltatások', '/szolgaltatasok'],
['Kapcsolat', '/kapcsolat'],
] as const
await page.goto('/')
for (const [label, expectedPath] of targets) {
await page.getByRole('navigation').getByRole('link', { name: label }).click()
await expect(page).toHaveURL(new RegExp(`${expectedPath.replace('/', '\\/')}$`))
await expect(page.getByRole('heading', { level: 1 })).toHaveCount(1)
}
})
test('SMOKE-04: service content and contact CTA are visible', async ({ page }) => {
await page.goto('/')
await expect(page.getByRole('heading', { name: 'Szolgáltatásaink' })).toBeVisible()
await expect(page.getByRole('link', { name: /kérjen ajánlatot/i })).toHaveAttribute('href', '/kapcsolat')
})
test('SMOKE-05: empty contact form is blocked client-side without API submission', async ({ page }) => {
let contactPostCount = 0
page.on('request', request => {
if (request.method() === 'POST' && new URL(request.url()).pathname === '/api/contact') contactPostCount++
})
await page.goto('/kapcsolat')
await page.getByRole('button', { name: 'Üzenet küldése' }).click()
await expect(page.getByRole('textbox', { name: 'Név *' })).toBeVisible()
await expect.poll(() => contactPostCount).toBe(0)
})
+63 -22
View File
@@ -16,6 +16,7 @@
},
"devDependencies": {
"@eslint/eslintrc": "^3",
"@playwright/test": "^1.62.1",
"@tailwindcss/postcss": "^4",
"@testing-library/jest-dom": "^6.5",
"@testing-library/react": "^16.0",
@@ -635,7 +636,6 @@
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.5.0.tgz",
"integrity": "sha512-sbP8GzB1WDzacS8fgNPpHlp6C9VZe+SJP3F90W9rLemaQj2PzIuTEl1qDOYQf58YIpyjViI24y9aPWCjEzY2cg==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
@@ -657,7 +657,6 @@
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.1.0.tgz",
"integrity": "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
@@ -1844,7 +1843,6 @@
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -1861,7 +1859,6 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -1878,7 +1875,6 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -1895,7 +1891,6 @@
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -1912,7 +1907,6 @@
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -1929,7 +1923,6 @@
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -1946,7 +1939,6 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -1963,7 +1955,6 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -1980,7 +1971,6 @@
"cpu": [
"ppc64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -1997,7 +1987,6 @@
"cpu": [
"riscv64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -2014,7 +2003,6 @@
"cpu": [
"s390x"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -2031,7 +2019,6 @@
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -2048,7 +2035,6 @@
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -2065,7 +2051,6 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -2082,7 +2067,6 @@
"cpu": [
"wasm32"
],
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
@@ -2096,7 +2080,6 @@
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.0.3.tgz",
"integrity": "sha512-rZxtMsLwjdXkMUGC3WwsPwLNVqVqnTJT6MNIB6e+5fhMcSCPP0AOsNWuMQ5mdCq6HNjs/ZeWAEchpqeprqBD2Q==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
@@ -2112,7 +2095,6 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -2129,7 +2111,6 @@
"cpu": [
"ia32"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -2146,7 +2127,6 @@
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -2361,6 +2341,22 @@
"node": ">=12.4.0"
}
},
"node_modules/@playwright/test": {
"version": "1.62.1",
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.62.1.tgz",
"integrity": "sha512-DTcUc8qii+cpHvtOwggMtBRMjKZHXYWdw8syRYu2vtzuq4Wxphqq4NfCs5Zt44L6mA8rfDfj+PHnxFc/FeK6mQ==",
"devOptional": true,
"license": "Apache-2.0",
"dependencies": {
"playwright": "1.62.1"
},
"bin": {
"playwright": "cli.js"
},
"engines": {
"node": ">=20"
}
},
"node_modules/@protobufjs/aspromise": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz",
@@ -2876,7 +2872,6 @@
"version": "0.10.0",
"resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.0.tgz",
"integrity": "sha512-VyyPYFlOMNylG45GoAe0xDoLwWuowvf92F9kySqzYh8vmYm7D2u4iUJKa1tOUpS70Ku13ASrOkS4ScXFsTaCNQ==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
@@ -9556,6 +9551,52 @@
"node": ">=8"
}
},
"node_modules/playwright": {
"version": "1.62.1",
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.62.1.tgz",
"integrity": "sha512-0M+L3LAD8/nm554LOla9Ayx0j0tmFZ0FBcoQ7F1VuVHpM/XpiC8RcDzBQB8W5+hA8L22THxELzeF+2WcUzvcLg==",
"devOptional": true,
"license": "Apache-2.0",
"dependencies": {
"playwright-core": "1.62.1"
},
"bin": {
"playwright": "cli.js"
},
"engines": {
"node": ">=20"
},
"optionalDependencies": {
"fsevents": "2.3.2"
}
},
"node_modules/playwright-core": {
"version": "1.62.1",
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.62.1.tgz",
"integrity": "sha512-wPYSwEBJY9GHraISXqyqtx0na0LpO3XEX7jNDhntbex7tzUS7kLnZsOlFruFJB4Hi/rhDMjXGqHewDZ68nYZVw==",
"devOptional": true,
"license": "Apache-2.0",
"bin": {
"playwright-core": "cli.js"
},
"engines": {
"node": ">=20"
}
},
"node_modules/playwright/node_modules/fsevents": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
"hasInstallScript": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
"node_modules/possible-typed-array-names": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz",
+2
View File
@@ -14,6 +14,7 @@
"test:browser": "jest --config jest.config.js src/__tests__/browser-integration.test.ts",
"test:integration": "INTEGRATION_TESTS=1 jest --config jest.config.integration.js src/__tests__/integration.test.ts",
"test:e2e": "E2E_TESTS=1 jest --config jest.config.integration.js src/__tests__/e2e-docker.test.ts",
"test:smoke:staging": "playwright test --config=playwright.config.ts",
"test:docker": "npm run test:integration && npm run test:e2e",
"test:all": "npm run test:unit && npm run test:browser && npm run test:docker",
"test:report": "npm test -- --json --outputFile=test-results.json --silent && node ../scripts/sync-test-management.js",
@@ -37,6 +38,7 @@
},
"devDependencies": {
"@eslint/eslintrc": "^3",
"@playwright/test": "^1.62.1",
"@tailwindcss/postcss": "^4",
"@testing-library/jest-dom": "^6.5",
"@testing-library/react": "^16.0",
+15
View File
@@ -0,0 +1,15 @@
import { defineConfig, devices } from '@playwright/test'
export default defineConfig({
testDir: './e2e',
timeout: 30_000,
retries: process.env.CI ? 1 : 0,
reporter: [['list'], ['html', { open: 'never' }]],
use: {
baseURL: process.env.BASE_URL || 'https://stage.mozdit.hu',
trace: 'retain-on-failure',
screenshot: 'only-on-failure',
video: 'retain-on-failure',
},
projects: [{ name: 'chromium', use: { ...devices['Desktop Chrome'] } }],
})
+21 -3
View File
@@ -3,14 +3,14 @@
* These tests run against the real services in the Docker stack
*/
import { MongoClient } from 'mongodb'
import { MongoClient, ObjectId } from 'mongodb'
const DOCKER_SERVICES = {
app: 'http://localhost:3000',
app: 'http://localhost:8080',
mongoExpress: 'http://localhost:8081',
grafana: 'http://localhost:3001',
loki: 'http://localhost:3100',
mongodb: 'mongodb://admin:password123@localhost:27017/admin'
mongodb: 'mongodb://admin:password123@localhost:27018/mozdit?authSource=admin'
}
describe('Docker Environment Integration Tests', () => {
@@ -170,6 +170,24 @@ describe('Docker Environment Integration Tests', () => {
if (response.status === 200) {
expect(result).toHaveProperty('message', 'Üzenet sikeresen elküldve!')
expect(result).toHaveProperty('timestamp')
expect(result).toHaveProperty('submissionId')
if (!mongoClient) {
mongoClient = new MongoClient(DOCKER_SERVICES.mongodb)
await mongoClient.connect()
}
const savedSubmission = await mongoClient
.db('mozdit')
.collection('contact_submissions')
.findOne({ _id: new ObjectId(result.submissionId) })
expect(savedSubmission).toEqual(expect.objectContaining({
name: contactData.name,
email: contactData.email,
subject: contactData.subject,
message: contactData.message,
gdprConsent: true,
status: 'new',
}))
} else {
expect(result).toHaveProperty('error')
expect(result.error).toContain('Túl sok')
+20 -9
View File
@@ -1,4 +1,5 @@
import { NextRequest, NextResponse } from 'next/server'
import { getCollection } from '@/lib/mongodb'
interface ContactFormData {
name: string
@@ -8,6 +9,16 @@ interface ContactFormData {
gdprConsent: boolean
}
interface ContactSubmission {
name: string
email: string
subject: string
message: string
gdprConsent: true
status: 'new'
createdAt: Date
}
// Simple spam protection - rate limiting by IP
const rateLimitMap = new Map<string, { count: number; timestamp: number }>()
const RATE_LIMIT_WINDOW = 60 * 1000 // 1 minute
@@ -93,20 +104,20 @@ export async function POST(request: NextRequest) {
)
}
// Log the contact form submission (in production, this would be sent via email or saved to database)
console.log('Contact form submission:', {
const submissions = await getCollection<ContactSubmission>('contact_submissions')
const submission: ContactSubmission = {
...sanitizedData,
timestamp: new Date().toISOString(),
ip: ip
})
// TODO: In production, implement actual email sending
// For now, we'll just simulate success
gdprConsent: true,
status: 'new',
createdAt: new Date(),
}
const result = await submissions.insertOne(submission)
return NextResponse.json(
{
message: 'Üzenet sikeresen elküldve!',
timestamp: new Date().toISOString()
timestamp: submission.createdAt.toISOString(),
submissionId: result.insertedId.toHexString()
},
{ status: 200 }
)
@@ -2,6 +2,9 @@
* Unit tests for Contact API route
* These tests focus on testing the business logic without complex mocking
*/
import { POST } from './route'
const mockInsertOne = jest.fn()
// Mock the logger to avoid complex setup
jest.mock('@/lib/logger', () => ({
@@ -12,7 +15,57 @@ jest.mock('@/lib/logger', () => ({
})
}))
jest.mock('@/lib/mongodb', () => ({
getCollection: jest.fn(async () => ({ insertOne: mockInsertOne })),
}))
describe('/api/contact Unit Tests', () => {
beforeEach(() => {
mockInsertOne.mockReset()
})
describe('MongoDB persistence', () => {
const validData = {
name: 'Test User',
email: 'test@example.com',
subject: 'Test Subject',
message: 'This is a test message with enough content',
gdprConsent: true,
}
it('persists a valid submission before reporting success', async () => {
mockInsertOne.mockResolvedValue({ insertedId: { toHexString: () => 'submission-123' } })
const request = {
headers: new Headers({ 'x-forwarded-for': 'persistence-success' }),
json: async () => validData,
} as any
const response = await POST(request)
const body = await response.json()
expect(response.status).toBe(200)
expect(body.submissionId).toBe('submission-123')
expect(mockInsertOne).toHaveBeenCalledWith(expect.objectContaining({
...validData,
status: 'new',
createdAt: expect.any(Date),
}))
})
it('returns a server error when persistence fails', async () => {
mockInsertOne.mockRejectedValue(new Error('MongoDB unavailable'))
const request = {
headers: new Headers({ 'x-forwarded-for': 'persistence-failure' }),
json: async () => validData,
} as any
const response = await POST(request)
expect(response.status).toBe(500)
expect(mockInsertOne).toHaveBeenCalledTimes(1)
})
})
// TC-001: Email Format Validation Test (ZEE-48)
describe('Input validation logic', () => {
it('should validate required fields', () => {
+1 -1
View File
@@ -145,7 +145,7 @@ export default function Home() {
</p>
</div>
<div className="grid grid-cols-1 md:grid-cols-3 gap-8 max-w-5xl mx-auto">
{pageContent.services.items.map((service: any, index: number) => (
{pageContent.services.items.map((service, index) => (
<div
key={service.id}
className="group card hover-lift"
+1 -1
View File
@@ -32,7 +32,7 @@ export default function ServicesPage() {
{/* Services Grid */}
<section className="max-w-6xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
{content.pages.home.services.items.map((service: any) => (
{content.pages.home.services.items.map((service) => (
<div key={service.id} className="bg-white rounded-xl shadow-sm border border-gray-200 p-8 hover:shadow-md transition-shadow">
<div className="w-16 h-16 bg-blue-100 rounded-lg flex items-center justify-center mb-6">
<span className="text-2xl">{service.icon}</span>
+17 -8
View File
@@ -23,6 +23,7 @@ import type {
ContactPageContent,
LegalPageContent
} from './types'
import { validateContent } from './schema'
// Import JSON files
import commonJson from './common.json'
@@ -33,14 +34,22 @@ import contactJson from './pages/contact.json'
import adatvedelemJson from './pages/adatvedelem.json'
import hasznalatiFeltetelekJson from './pages/hasznalati-feltetelek.json'
// Type assertions for JSON imports
const common: CommonContent = commonJson as CommonContent
const home: HomePageContent = homeJson as HomePageContent
const about: AboutPageContent = aboutJson as AboutPageContent
const services: ServicesPageContent = servicesJson as ServicesPageContent
const contact: ContactPageContent = contactJson as ContactPageContent
const adatvedelem: LegalPageContent = adatvedelemJson as LegalPageContent
const hasznalatiFeltetelek: LegalPageContent = hasznalatiFeltetelekJson as LegalPageContent
function loadValidatedContent<T>(fileKey: string, value: unknown): T {
const result = validateContent(fileKey, value)
if (!result.ok) {
throw new Error(`Érvénytelen content JSON (${fileKey}): ${result.errors.join('; ')}`)
}
return value as T
}
// Runtime validation prevents malformed JSON from silently reaching the UI.
const common = loadValidatedContent<CommonContent>('common', commonJson)
const home = loadValidatedContent<HomePageContent>('home', homeJson)
const about = loadValidatedContent<AboutPageContent>('about', aboutJson)
const services = loadValidatedContent<ServicesPageContent>('services', servicesJson)
const contact = loadValidatedContent<ContactPageContent>('contact', contactJson)
const adatvedelem = loadValidatedContent<LegalPageContent>('adatvedelem', adatvedelemJson)
const hasznalatiFeltetelek = loadValidatedContent<LegalPageContent>('hasznalatiFeltetelek', hasznalatiFeltetelekJson)
// Combined content object
export const content: SiteContent = {
+25 -31
View File
@@ -1,55 +1,49 @@
{
"meta": {
"0": {
"$1escription": "Ismerje meg a mozdIT Bt. történetét, küldetését és értékeit. Több mint 20 éve nyújtunk megbízható IT szolgáltatásokat."
},
"title": "Rólunk",
"description": "Ismerje meg a mozdIT Bt. történetét, küldetését és értékeit. Több mint 20 éve nyújtunk megbízható IT szolgáltatásokat.",
"ogDescription": "Ismerje meg a mozdIT Bt. történetét, küldetését és értékeit."
},
"hero": {
"title": "Rólunk",
"subtitle": "Több mint 20 éve biztosítunk megbízható IT infrastruktúrát."
"subtitle": "Több mint 20 éve biztosítunk megbízható IT infrastruktúrát és személyes ügyfélszolgálatot"
},
"story": {
"title": "Történetünk",
"paragraphs": [],
"paragraphs[0]": "Kezdetben néhány ügyfél weboldalának üzemeltetésével indultunk, ma pedig számos domain és email fiók működését biztosítjuk. Növekedésünk során mindig szem előtt tartottuk az alapelveinket: megbízhatóság, személyes kapcsolat és műszaki kiválóság.",
"paragraphs[1]": "Ez egy kis cég, egy dedikált szerverünk van, és ezen szolgáltatunk webtárhelyet, domain-fenntartást és e-mail szolgáltatásokat kis- és középvállalkozásoknak, illetve magánszemélyeknek.",
"paragraphs[2]": "Kis csapat, nagy elköteleződés. Saját dedikált infrastruktúránkon garantáljuk az adatbiztonságot és a folyamatos rendelkezésre állást. Nálunk nincs technoblabla: személyes ügyfélszolgálat, emberi lépték és mindig elérhető válaszok várják."
"paragraphs": [
"Kezdetben néhány ügyfél weboldalának üzemeltetésével indultunk, ma pedig számos domain és email fiók működését biztosítjuk. Növekedésünk során mindig szem előtt tartottuk az alapelveinket: megbízhatóság, személyes kapcsolat és műszaki kiválóság.",
"Ez egy kis cég, egy dedikált szerverünk van, és ezen szolgáltatunk webtárhelyet, domain-fenntartást és e-mail szolgáltatásokat kis- és középvállalkozásoknak, illetve magánszemélyeknek.",
"Kis csapat, nagy elköteleződés. Saját dedikált infrastruktúránkon garantáljuk az adatbiztonságot és a folyamatos rendelkezésre állást. Nálunk nincs technoblabla: személyes ügyfélszolgálat, emberi lépték és mindig elérhető válaszok várják."
]
},
"mission": {
"title": "Küldetésünk és értékeink",
"subtitle": "Minden nap azért dolgozunk, hogy ügyfeleink digitális jelenléte biztonságos, stabil és hatékony legyen.",
"values": [],
"values[0]": {
"0": {
"$1escription": "99.9% uptime és 24/7 monitoring biztosítja, hogy szolgáltatásaink mindig elérhetők legyenek."
"values": [
{
"title": "Megbízhatóság",
"description": "99.9% uptime és 24/7 monitoring biztosítja, hogy szolgáltatásaink mindig elérhetők legyenek.",
"icon": "🛡️"
},
"title": "Megbízhatóság",
"icon": "🛡️"
},
"values[1]": {
"0": {
"$1escription": "Minden ügyfél számít számunkra. Személyre szabott megoldásokat kínálunk."
{
"title": "Személyes kapcsolat",
"description": "Minden ügyfél számít számunkra. Személyre szabott megoldásokat kínálunk.",
"icon": "👥"
},
"title": "Személyes kapcsolat",
"icon": "👥"
},
"values[2]": {
"0": {
"$1escription": "Korszerű technológiák és bevált gyakorlatok alkalmazásával biztosítjuk a legmagasabb színvonalú szolgáltatást."
},
"title": "Műszaki kiválóság",
"icon": "⚡"
}
{
"title": "Műszaki kiválóság",
"description": "Korszerű technológiák és bevált gyakorlatok alkalmazásával biztosítjuk a legmagasabb színvonalú szolgáltatást.",
"icon": "⚡"
}
]
},
"team": {
"title": "Szakértő csapat",
"subtitle": "Tapasztalt IT szakemberek, akik elkötelezetten és megbízhatóan dolgoznak az ügyfeleink sikeréért.",
"paragraphs": [],
"paragraphs[0]": "Csapatunk tagjai több mint 20 éves tapasztalattal rendelkeznek a maguk területén, és folyamatosan követik a technológiai újdonságokat.",
"paragraphs[1]": "Hiszünk abban, hogy a jó kommunikáció és a műszaki tudás együtt teremti meg a tökéletes ügyfélélményt."
"paragraphs": [
"Csapatunk tagjai több mint 20 éves tapasztalattal rendelkeznek a maguk területén, és folyamatosan követik a technológiai újdonságokat.",
"Hiszünk abban, hogy a jó kommunikáció és a műszaki tudás együtt teremti meg a tökéletes ügyfélélményt."
]
},
"cta": {
"title": "Legyen Ön is elégedett ügyfelünk!",
+26 -40
View File
@@ -1,45 +1,31 @@
{
"title": "Adatvédelmi Tájékoztató",
"lastUpdated": "2026. április 26.",
"sections": [],
"lastUp": {
"$1ate": {
"$1": "2026. április 26."
"sections": [
{
"id": "bevezetes",
"title": "1. Bevezetés",
"content": "A mozdIT Bt. (székhely: Szigetszentmiklós, Magyarország; e-mail: info@mozdit.hu) mint adatkezelő elkötelezett a felhasználók személyes adatainak védelme iránt. Jelen tájékoztató célja, hogy az Európai Unió Általános Adatvédelmi Rendelete (GDPR) előírásainak megfelelően átlátható módon bemutassa cégünk adatkezelési gyakorlatát."
},
{
"id": "adatkezeles-celja",
"title": "2. Milyen adatokat kezelünk, mi a céljuk és a jogalapjuk?",
"content": "A szolgáltatásaink nyújtása során az alábbi adatokat kezeljük:\n\n• **Kapcsolatfelvétel és űrlapok:** Név, e-mail cím, telefonszám, a beküldött üzenet tartalma. (Cél: ügyfélkapcsolat, árajánlatadás. Jogalap: hozzájárulás és szerződést megelőző lépések.)\n• **Ügyfél-nyilvántartás és számlázás:** Számlázási név és cím, adószám, kapcsolattartó adatai. (Cél: szolgáltatás biztosítása, számlázás. Jogalap: jogi kötelezettség, szerződés teljesítése.)\n• **Webtárhely és e-mail szolgáltatás:** Technikai naplófájlok (IP címek, belépési időpontok), e-mail forgalmi adatok szűrése (spam, vírusok). (Cél: IT biztonság fenntartása, szolgáltatás biztosítása. Jogalap: szerződés teljesítése, jogos érdek.)\n• **Domain-regisztráció:** Tulajdonosi és kapcsolattartói adatok. (Cél: domain delegáció ügyintézése a regisztrátorok felé. Jogalap: szerződés teljesítése.)\n• **Sütik (Cookies):** Weboldalunk a megfelelő technikai működés érdekében alapvető (munkamenet) sütiket használ. Harmadik fél analitikai kódjait (pl. Meta pixel) jelenleg nem használjuk az alapvető nyomkövetéshez."
},
{
"id": "adattovabbitas",
"title": "3. Adattovábbítás és adatfeldolgozók",
"content": "A személyes adatokat bizalmasan kezeljük, és alapvetően a mozdIT Bt. saját, dedikált infrastruktúráján tároljuk. Harmadik félnek csak jogszabályi kötelezettség alapján (pl. NAV részére könyvelési okokból), illetve a domain-regisztrációs szolgáltatások keretében továbbítunk adatokat az illetékes regisztrátorok (pl. ISZT) felé."
},
{
"id": "adatmegorzes",
"title": "4. Az adatkezelés időtartama",
"content": "A számviteli bizonylatokat a Számviteli törvény értelmében 8 évig megőrizzük. A kapcsolatfelvételi űrlapon megadott, de szerződéssel nem végződő adatokat az ügy lezárását követő 1 év elteltével töröljük. A webszerverek technikai naplófájljait (logokat) a biztonsági és üzemeltetési feladatok ellátása céljából legfeljebb 30 napig őrizzük meg."
},
{
"id": "jogok",
"title": "5. Az érintettek jogai",
"content": "Az érintettek bármikor tájékoztatást kérhetnek adataik kezeléséről, kérhetik azok helyesbítését, törlését (a kötelező adatkezelések kivételével) vagy zárolását. Kérésüket az info@mozdit.hu e-mail címre küldhetik el, amire indokolatlan késedelem nélkül, de legkésőbb egy hónapon belül válaszolunk. Jogosulatlan adatkezelés esetén a Nemzeti Adatvédelmi és Információszabadság Hatósághoz (NAIH) lehet fordulni panasszal."
}
},
"sections[0]": {
"i": {
"$1": "bevezetes"
},
"title": "1. Bevezetés",
"content": "A mozdIT Bt. (székhely: Szigetszentmiklós, Magyarország; e-mail: info@mozdit.hu) mint adatkezelő elkötelezett a felhasználók személyes adatainak védelme iránt. Jelen tájékoztató célja, hogy az Európai Unió Általános Adatvédelmi Rendelete (GDPR) előírásainak megfelelően átlátható módon bemutassa cégünk adatkezelési gyakorlatát."
},
"sections[1]": {
"i": {
"$1": "adatkezeles-celja"
},
"title": "2. Milyen adatokat kezelünk, mi a céljuk és a jogalapjuk?",
"content": "A szolgáltatásaink nyújtása során az alábbi adatokat kezeljük:\n\n• **Kapcsolatfelvétel és űrlapok:** Név, e-mail cím, telefonszám, a beküldött üzenet tartalma. (Cél: ügyfélkapcsolat, árajánlatadás. Jogalap: hozzájárulás és szerződést megelőző lépések.)\n• **Ügyfél-nyilvántartás és számlázás:** Számlázási név és cím, adószám, kapcsolattartó adatai. (Cél: szolgáltatás biztosítása, számlázás. Jogalap: jogi kötelezettség, szerződés teljesítése.)\n• **Webtárhely és e-mail szolgáltatás:** Technikai naplófájlok (IP címek, belépési időpontok), e-mail forgalmi adatok szűrése (spam, vírusok). (Cél: IT biztonság fenntartása, szolgáltatás biztosítása. Jogalap: szerződés teljesítése, jogos érdek.)\n• **Domain-regisztráció:** Tulajdonosi és kapcsolattartói adatok. (Cél: domain delegáció ügyintézése a regisztrátorok felé. Jogalap: szerződés teljesítése.)\n• **Sütik (Cookies):** Weboldalunk a megfelelő technikai működés érdekében alapvető (munkamenet) sütiket használ. Harmadik fél analitikai kódjait (pl. Meta pixel) jelenleg nem használjuk az alapvető nyomkövetéshez."
},
"sections[2]": {
"i": {
"$1": "adattovabbitas"
},
"title": "3. Adattovábbítás és adatfeldolgozók",
"content": "A személyes adatokat bizalmasan kezeljük, és alapvetően a mozdIT Bt. saját, dedikált infrastruktúráján tároljuk. Harmadik félnek csak jogszabályi kötelezettség alapján (pl. NAV részére könyvelési okokból), illetve a domain-regisztrációs szolgáltatások keretében továbbítunk adatokat az illetékes regisztrátorok (pl. ISZT) felé."
},
"sections[3]": {
"i": {
"$1": "adatmegorzes"
},
"title": "4. Az adatkezelés időtartama",
"content": "A számviteli bizonylatokat a Számviteli törvény értelmében 8 évig megőrizzük. A kapcsolatfelvételi űrlapon megadott, de szerződéssel nem végződő adatokat az ügy lezárását követő 1 év elteltével töröljük. A webszerverek technikai naplófájljait (logokat) a biztonsági és üzemeltetési feladatok ellátása céljából legfeljebb 30 napig őrizzük meg."
},
"sections[4]": {
"i": {
"$1": "jogok"
},
"title": "5. Az érintettek jogai",
"content": "Az érintettek bármikor tájékoztatást kérhetnek adataik kezeléséről, kérhetik azok helyesbítését, törlését (a kötelező adatkezelések kivételével) vagy zárolását. Kérésüket az info@mozdit.hu e-mail címre küldhetik el, amire indokolatlan késedelem nélkül, de legkésőbb egy hónapon belül válaszolunk. Jogosulatlan adatkezelés esetén a Nemzeti Adatvédelmi és Információszabadság Hatósághoz (NAIH) lehet fordulni panasszal."
}
]
}
@@ -1,45 +1,31 @@
{
"title": "Használati Feltételek (ÁSZF)",
"lastUpdated": "2026. április 26.",
"sections": [],
"lastUp": {
"$1ate": {
"$1": "2026. április 26."
"sections": [
{
"id": "bevezetes",
"title": "1. Általános rendelkezések",
"content": "Jelen Általános Szerződési Feltételek (ÁSZF) a mozdIT Bt. (székhely: Szigetszentmiklós, Magyarország; e-mail: info@mozdit.hu) által nyújtott webtárhely, e-mail szolgáltatás és domain regisztráció szolgáltatásainak igénybevételére vonatkozó szabályokat tartalmazzák. A szolgáltatások megrendelésével és használatával az Ügyfél elfogadja jelen feltételeket."
},
{
"id": "szolgaltatasok",
"title": "2. Szolgáltatások leírása",
"content": "A mozdIT Bt. dedikált szerverein nyújt webtárhely, e-mail fiók és DNS szolgáltatásokat. A szolgáltatások garantált rendelkezésre állása 99,9% éves szinten. A szolgáltató automatikus biztonsági mentéseket végez, de javasolja az Ügyfél számára is az adatai rendszeres mentését."
},
{
"id": "ugyfel-kotelezettsegei",
"title": "3. Az Ügyfél kötelezettségei",
"content": "Az Ügyfél köteles a szolgáltatásokat rendeltetésszerűen használni. Szigorúan tilos a tárhelyen és e-mail fiókokban illegális, szerzői jogot sértő tartalmak elhelyezése, valamint spam (kéretlen reklámlevél) tömeges küldése. A Szolgáltató fenntartja a jogot, hogy jogsértés vagy hálózatot veszélyeztető tevékenység (pl. DDoS forrás) észlelése esetén a szolgáltatást előzetes értesítés nélkül azonnal szüneteltesse vagy megszüntesse."
},
{
"id": "dijazas",
"title": "4. Díjazás és fizetési feltételek",
"content": "A szolgáltatások díjai a felek által megkötött egyedi megállapodás vagy elfogadott árajánlat alapján fizetendők. A számlázás elektronikus úton történik. A fizetési határidő elmulasztása esetén a Szolgáltató jogosult a szolgáltatás szüneteltetésére, melyről az Ügyfelet előzetesen e-mailben értesíti."
},
{
"id": "felelosseg",
"title": "5. Felelősségkorlátozás",
"content": "A Szolgáltató mindent megtesz az adatok biztonsága és a folyamatos üzemeltetés érdekében, ugyanakkor nem vállal felelősséget a vis maior, illetve a harmadik felek által okozott (pl. hálózati) kimaradások miatti esetleges adatvesztésért vagy elmaradt haszonért."
}
},
"sections[0]": {
"i": {
"$1": "bevezetes"
},
"title": "1. Általános rendelkezések",
"content": "Jelen Általános Szerződési Feltételek (ÁSZF) a mozdIT Bt. (székhely: Szigetszentmiklós, Magyarország; e-mail: info@mozdit.hu) által nyújtott webtárhely, e-mail szolgáltatás és domain regisztráció szolgáltatásainak igénybevételére vonatkozó szabályokat tartalmazzák. A szolgáltatások megrendelésével és használatával az Ügyfél elfogadja jelen feltételeket."
},
"sections[1]": {
"i": {
"$1": "szolgaltatasok"
},
"title": "2. Szolgáltatások leírása",
"content": "A mozdIT Bt. dedikált szerverein nyújt webtárhely, e-mail fiók és DNS szolgáltatásokat. A szolgáltatások garantált rendelkezésre állása 99,9% éves szinten. A szolgáltató automatikus biztonsági mentéseket végez, de javasolja az Ügyfél számára is az adatai rendszeres mentését."
},
"sections[2]": {
"i": {
"$1": "ugyfel-kotelezettsegei"
},
"title": "3. Az Ügyfél kötelezettségei",
"content": "Az Ügyfél köteles a szolgáltatásokat rendeltetésszerűen használni. Szigorúan tilos a tárhelyen és e-mail fiókokban illegális, szerzői jogot sértő tartalmak elhelyezése, valamint spam (kéretlen reklámlevél) tömeges küldése. A Szolgáltató fenntartja a jogot, hogy jogsértés vagy hálózatot veszélyeztető tevékenység (pl. DDoS forrás) észlelése esetén a szolgáltatást előzetes értesítés nélkül azonnal szüneteltesse vagy megszüntesse."
},
"sections[3]": {
"i": {
"$1": "dijazas"
},
"title": "4. Díjazás és fizetési feltételek",
"content": "A szolgáltatások díjai a felek által megkötött egyedi megállapodás vagy elfogadott árajánlat alapján fizetendők. A számlázás elektronikus úton történik. A fizetési határidő elmulasztása esetén a Szolgáltató jogosult a szolgáltatás szüneteltetésére, melyről az Ügyfelet előzetesen e-mailben értesíti."
},
"sections[4]": {
"i": {
"$1": "felelosseg"
},
"title": "5. Felelősségkorlátozás",
"content": "A Szolgáltató mindent megtesz az adatok biztonsága és a folyamatos üzemeltetés érdekében, ugyanakkor nem vállal felelősséget a vis maior, illetve a harmadik felek által okozott (pl. hálózati) kimaradások miatti esetleges adatvesztésért vagy elmaradt haszonért."
}
]
}
+70 -90
View File
@@ -1,119 +1,99 @@
{
"hero": {
"0": {
"$1escription": "A mozdIT Bt. célja, hogy megbízható, támogató szolgáltatásokkal segítse ügyfeleit a digitális térben való sikerben."
},
"title": "Web és e-mail testreszabott szolgáltatásokkal",
"title": "Megbízható web és emailszolgáltatás személyre szabott támogatással",
"subtitle": "Kis ügyfélkör, nagy figyelem: stabil tárhely, üzembiztos levelezés és DNS adminisztráció — gyors reakcióval.",
"description": "A mozdIT Bt. célja, hogy megbízható, támogató szolgáltatásokkal segítse ügyfeleit a digitális térben való sikerben.",
"trustBullets": [],
"trustBullets": [
"Saját dedikált infrastruktúra",
"Webtárhely, domain, e-mail egy helyen",
"Kifejezetten kisvállalkozásokra szabva"
],
"cta": {
"primary": {
"text": "Beszéljük át, mire van szüksége",
"href": "/kapcsolat",
"external": "false"
"external": false
},
"secondary": {
"text": "Webmail Ugrás",
"href": "https://mail.mozdit.hu",
"external": true
},
"secon": {
"$1ary": {
"text": "mozdIT Webmail",
"href": "https://mail.mozdit.hu",
"external": "true"
}
}
},
"trustBullets[0]": "Saját dedikált infrastruktúra",
"trustBullets[1]": "Webtárhely, domain, e-mail egy helyen",
"trustBullets[2]": "Kisvállalkozásokra szabva"
}
},
"about": {
"title": "Miért érdemes minket választani?",
"usps": [],
"usps[0]": {
"0": {
"$1escription": "Nincs technoblabla. Átlátható és könnyen használható rendszereket biztosítunk, hogy Ön a saját üzletére fókuszálhasson."
"usps": [
{
"id": "egyszeru",
"title": "Egyszerűen kezelhető szolgáltatások",
"description": "Nincs technoblabla. Átlátható és könnyen használható rendszereket biztosítunk, hogy Ön a saját üzletére fókuszálhasson.",
"icon": "🎯"
},
"i": {
"$1": "egyszeru"
{
"id": "nemtomeg",
"title": "Nem tömegszolgáltatás",
"description": "Egyedi figyelem és rugalmas, személyre szabott csomagok, amelyek tökéletesen igazodnak a vállalkozása igényeihez.",
"icon": "👤"
},
"title": "Egyszerűen kezelhető szolgáltatások",
"icon": "🎯"
},
"usps[1]": {
"0": {
"$1escription": "Egyedi figyelem és rugalmas, személyre szabott csomagok, amelyek tökéletesen igazodnak a vállalkozása igényeihez."
{
"id": "egykezben",
"title": "Minden egy kézben",
"description": "Domain, webtárhely és üzleti e-mail szolgáltatások zökkenőmentes kezelése, mindent egyetlen megbízható helyről.",
"icon": "🧩"
},
"i": {
"$1": "nemtomeg"
},
"title": "Nem tömegszolgáltatás",
"icon": "👤"
},
"usps[2]": {
"0": {
"$1escription": "Domain, webtárhely és üzleti e-mail szolgáltatások zökkenőmentes kezelése, mindent egyetlen megbízható helyről."
},
"i": {
"$1": "egykezben"
},
"title": "Minden egy kézben",
"icon": "🧩"
},
"usps[3]": {
"0": {
"$1escription": "TLS/SSL védelemmel, modern biztonsági protokollokkal és gyors (SSD) kiszolgálással biztosítjuk a maximális teljesítményt."
},
"i": {
"$1": "modern_tech"
},
"title": "Modern és Biztonságos",
"icon": "🚀"
}
{
"id": "modern_tech",
"title": "Modern és Biztonságos",
"description": "TLS/SSL védelemmel, modern biztonsági protokollokkal és gyors (SSD) kiszolgálással biztosítjuk a maximális teljesítményt.",
"icon": "🚀"
}
]
},
"services": {
"title": "Szolgáltatásaink",
"subtitle": "Komplex megoldásokat kínálunk, amelyek tökéletesen igazodnak a vállalkozás igényeihez.",
"items": [],
"items[0]": {
"0": {
"$1escription": "Stabil és gyors tárhely szolgáltatás saját infrastruktúrán, biztonsági mentésekkel és magas rendelkezésre állással."
"items": [
{
"id": "hosting",
"title": "Webtárhely (Hosting)",
"description": "Stabil és gyors tárhely szolgáltatás saját infrastruktúrán, biztonsági mentésekkel és magas rendelkezésre állással.",
"icon": "🔥",
"features": [
"99.9% uptime garancia",
"SSD alapú gyors kiszolgálás",
"24/7 rendszeres monitoring",
"Automatikus backup rendszer"
],
"ctaText": "További információk"
},
"i": {
"$1": "hosting"
{
"id": "email",
"title": "E-mail szolgáltatás",
"description": "Üzleti, saját domain alapú levelezés magas szintű spamszűréssel, bárhonnan elérhető webmail felülettel.",
"icon": "📧",
"features": [
"Saját domain alapú e-mail címek",
"Biztonságos webmail és IMAP/POP3",
"Erős spam- és vírusvédelem",
"Nagy tárterület"
],
"ctaText": "További információk"
},
"title": "Webtárhely (Hosting)",
"icon": "🔥",
"features": "99.9% uptime garancia,SSD alapú gyors kiszolgálás,24/7 rendszeres monitoring,Automatikus backup rendszer",
"ctaText": "További információk"
},
"items[1]": {
"0": {
"$1escription": "Üzleti, saját domain alapú levelezés magas szintű spamszűréssel, bárhonnan elérhető webmail felülettel."
},
"i": {
"$1": "email"
},
"title": "E-mail szolgáltatás",
"icon": "📧",
"features": "Saját domain alapú e-mail címek,Biztonságos webmail és IMAP/POP3,Erős spam- és vírusvédelem,Nagy tárterület",
"ctaText": "További információk"
},
"items[2]": {
"0": {
"$1escription": "Teljes körű domain regisztráció, kezelés és biztonságos névszerver (DNS) adminisztráció."
},
"i": {
"$1": "domain"
},
"title": "Domain és DNS",
"icon": "🌐",
"features": "Magyar és nemzetközi domainek,Biztonságos DNS kezelés,Email routing (SPF, DKIM, DMARC),Gyors ügyintézés",
"ctaText": "További információk"
}
{
"id": "domain",
"title": "Domain és DNS",
"description": "Teljes körű domain regisztráció, kezelés és biztonságos névszerver (DNS) adminisztráció.",
"icon": "🌐",
"features": [
"Magyar és nemzetközi domainek",
"Biztonságos DNS kezelés",
"Email routing (SPF, DKIM, DMARC)",
"Gyors ügyintézés"
],
"ctaText": "További információk"
}
]
},
"cta": {
"title": "Kérjen személyre szabott ajánlatot",
+69 -59
View File
@@ -1,41 +1,8 @@
{
"0": {
"$1etails": {
"title": "Részletes szolgáltatásleírás",
"subtitle": "Minden szolgáltatásunk mögött évtizedes tapasztalat és modern technológia áll",
"services[0]": {
"0": {
"$1escription": "Weboldalak biztonságos és gyors üzemeltetése SSD tárolással, automatikus biztonsági mentéssel és 24/7 monitoringgal. Támogatjuk a PHP, Python, Node.js technológiákat és MySQL/PostgreSQL adatbázisokat."
},
"icon": "🌐",
"title": "Web Hosting részletesen",
"specs": "[object Object]"
},
"services[1]": {
"0": {
"$1escription": "Professzionális email fiókok saját domain névvel, spam szűréssel és vírusvédelemmel. Webmail felület és IMAP/POP3/SMTP támogatás minden népszerű email klienssel."
},
"icon": "✉️",
"title": "Email szolgáltatás részletesen",
"specs": "[object Object]"
},
"services[2]": {
"0": {
"$1escription": "Teljes DNS kezelés domain regisztrációval, átvitellel és professzionális beállításokkal. Gyors propagáció és megbízható névszerverek világszerte."
},
"icon": "⚙️",
"title": "DNS adminisztráció részletesen",
"specs": "[object Object]"
}
}
},
"meta": {
"0": {
"$1escription": "Webhosting, email szolgáltatás és DNS adminisztráció. Ismerje meg részletes szolgáltatásainkat."
},
"title": "Szolgáltatások",
"description": "Webhosting, email szolgáltatás és DNS adminisztráció professzionális szinten. Ismerje meg részletes szolgáltatásainkat.",
"ogDescription": "Webhosting, email szolgáltatás és DNS adminisztráció."
"ogDescription": "Webhosting, email szolgáltatás és DNS adminisztráció professzionális szinten."
},
"hero": {
"title": "Szolgáltatásaink",
@@ -44,41 +11,84 @@
"details": {
"title": "Részletes szolgáltatásleírás",
"subtitle": "Minden szolgáltatásunk mögött évtizedes tapasztalat és modern technológia áll",
"services": []
"services": [
{
"icon": "🌐",
"title": "Web Hosting részletesen",
"description": "Weboldalak biztonságos és gyors üzemeltetése SSD tárolással, automatikus biztonsági mentéssel és 24/7 monitoringgal. Támogatjuk a PHP, Python, Node.js technológiákat és MySQL/PostgreSQL adatbázisokat.",
"specs": {
"title": "Technikai specifikációk:",
"items": [
"SSD tárhely 10GB-tól 500GB-ig",
"Havi adatforgalom: korlátlan",
"SSL tanúsítványok (Let's Encrypt vagy prémium)",
"CDN integráció a gyorsabb betöltésért",
"Automatikus napi biztonsági mentés",
"cPanel vagy egyedi admin felület"
]
}
},
{
"icon": "✉️",
"title": "Email szolgáltatás részletesen",
"description": "Professzionális email fiókok saját domain névvel, spam szűréssel és vírusvédelemmel. Webmail felület és IMAP/POP3/SMTP támogatás minden népszerű email klienssel.",
"specs": {
"title": "Email funkciók:",
"items": [
"Korlátlan email fiókok létrehozása",
"5GB-50GB tárhelyet fiókként",
"Webmail hozzáférés (Roundcube/SOGo)",
"Mobilalkalmazás szinkronizáció",
"Spam és vírusszűrés",
"Email továbbítás és automatikus válaszok",
"Backup és archiválás"
]
}
},
{
"icon": "⚙️",
"title": "DNS adminisztráció részletesen",
"description": "Teljes DNS kezelés domain regisztrációval, átvitellel és professzionális beállításokkal. Gyors propagáció és megbízható névszerverek világszerte.",
"specs": {
"title": "DNS szolgáltatások:",
"items": [
"Domain regisztráció (.hu, .com, .eu, stb.)",
"Domain átvitel más szolgáltatótól",
"DNS rekord kezelés (A, CNAME, MX, TXT)",
"Subdomain beállítások",
"Redirect és forwarding szolgáltatások",
"DNSSEC támogatás",
"API hozzáférés fejlesztőknek"
]
}
}
]
},
"support": {
"title": "Műszaki támogatás",
"subtitle": "Minden szolgáltatásunkhoz teljes körű műszaki támogatást biztosítunk",
"channels": [],
"channels[0]": {
"0": {
"$1escription": "24 órán belüli válasz"
"channels": [
{
"icon": "✉️",
"title": "Email támogatás",
"description": "24 órán belüli válasz"
},
"icon": "✉️",
"title": "Email támogatás"
},
"channels[1]": {
"0": {
"$1escription": "Munkaidőben elérhető"
{
"icon": "📞",
"title": "Telefonos segítség",
"description": "Munkaidőben elérhető"
},
"icon": "📞",
"title": "Telefonos segítség"
},
"channels[2]": {
"0": {
"$1escription": "Azonnali beavatkozás"
},
"icon": "🚨",
"title": "Sürgős esetek"
}
{
"icon": "🚨",
"title": "Sürgős esetek",
"description": "Azonnali beavatkozás"
}
]
},
"cta": {
"title": "Kezdjük el a közös munkát!",
"subtitle": "Vegye fel velünk a kapcsolatot ingyenes konzultációért és egyedi ajánlatért.",
"primaryButton": "Kapcsolatfelvétel",
"secondaryButton": "Webmail belépés",
"secon": {
"$1aryButton": "Webmail belépés"
}
"secondaryButton": "Webmail belépés"
}
}
+102
View File
@@ -0,0 +1,102 @@
// Shared runtime schema for the content JSON files.
// Kept dependency-free so it can run in both Next.js and content-editor.js.
const string = { type: 'string' };
const boolean = { type: 'boolean' };
const array = items => ({ type: 'array', items });
const object = properties => ({ type: 'object', properties, required: Object.keys(properties), additionalProperties: false });
const ctaLink = object({ text: string, href: string, external: boolean });
const iconText = object({ icon: string, title: string, description: string });
const legal = object({ id: string, title: string, content: string });
const schemas = {
common: object({
buttons: object({ contact: string, learnMore: string, webmail: string, sendMessage: string }),
labels: object({ required: string, features: string }),
validation: object({ required: string, invalidEmail: string, minLength: string }),
}),
home: object({
hero: object({ title: string, subtitle: string, description: string, trustBullets: array(string), cta: object({ primary: ctaLink, secondary: ctaLink }) }),
about: object({ title: string, usps: array(object({ id: string, title: string, description: string, icon: string })) }),
services: object({ title: string, subtitle: string, items: array(object({ id: string, title: string, description: string, icon: string, features: array(string), ctaText: string })) }),
cta: object({ title: string, subtitle: string, button: string }),
serviceFeatures: object({ title: string }),
}),
about: object({
meta: object({ title: string, description: string, ogDescription: string }),
hero: object({ title: string, subtitle: string }),
story: object({ title: string, paragraphs: array(string) }),
mission: object({ title: string, subtitle: string, values: array(iconText) }),
team: object({ title: string, subtitle: string, paragraphs: array(string) }),
cta: object({ title: string, subtitle: string, button: string }),
}),
services: object({
meta: object({ title: string, description: string, ogDescription: string }),
hero: object({ title: string, subtitle: string }),
details: object({ title: string, subtitle: string, services: array(object({ icon: string, title: string, description: string, specs: object({ title: string, items: array(string) }) })) }),
support: object({ title: string, subtitle: string, channels: array(iconText) }),
cta: object({ title: string, subtitle: string, primaryButton: string, secondaryButton: string }),
}),
contact: object({
meta: object({ title: string, description: string }),
hero: object({ title: string, subtitle: string }),
form: object({
title: string, successMessage: string, errorMessage: string,
fields: object({
name: object({ label: string, placeholder: string, error: string }),
email: object({ label: string, placeholder: string, errorRequired: string, errorInvalid: string }),
subject: object({ label: string, placeholder: string, error: string }),
message: object({ label: string, placeholder: string, errorRequired: string, errorMinLength: string }),
gdpr: object({ label: string, error: string }),
}),
submitButton: string, submittingButton: string,
}),
info: object({
title: string,
email: object({ title: string, responseTime: string }),
company: object({ title: string }),
webmail: object({ title: string, linkText: string, subtitle: string }),
}),
faq: object({ title: string, items: array(object({ question: string, answer: string })) }),
}),
adatvedelem: object({ title: string, lastUpdated: string, sections: array(legal) }),
hasznalatiFeltetelek: object({ title: string, lastUpdated: string, sections: array(legal) }),
};
function validateNode(value, schema, path, errors) {
if (schema.type === 'string' || schema.type === 'boolean') {
if (typeof value !== schema.type) errors.push(`${path}: ${schema.type} érték szükséges`);
return;
}
if (schema.type === 'array') {
if (!Array.isArray(value)) {
errors.push(`${path}: tömb szükséges`);
return;
}
value.forEach((item, index) => validateNode(item, schema.items, `${path}[${index}]`, errors));
return;
}
if (!value || typeof value !== 'object' || Array.isArray(value)) {
errors.push(`${path}: objektum szükséges`);
return;
}
for (const key of schema.required) {
if (!(key in value)) errors.push(`${path}.${key}: kötelező mező hiányzik`);
}
for (const key of Object.keys(value)) {
if (!(key in schema.properties)) errors.push(`${path}.${key}: nem engedélyezett mező`);
}
for (const [key, child] of Object.entries(schema.properties)) {
if (key in value) validateNode(value[key], child, `${path}.${key}`, errors);
}
}
function validateContent(fileKey, data) {
const schema = schemas[fileKey];
if (!schema) return { ok: false, errors: [`Ismeretlen content fájl: ${fileKey}`] };
const errors = [];
validateNode(data, schema, '$', errors);
return { ok: errors.length === 0, errors };
}
module.exports = { schemas, validateContent };
+26 -1
View File
@@ -28,6 +28,32 @@ export interface TechSpec {
// Page-specific content types
export interface HomePageContent {
hero: {
title: string
subtitle: string
description: string
trustBullets: string[]
cta: {
primary: CTAContent
secondary: CTAContent
}
}
about: {
title: string
usps: Array<IconTextItem & { id: string }>
}
services: {
title: string
subtitle: string
items: Array<{
id: string
title: string
description: string
icon: string
features: string[]
ctaText: string
}>
}
cta: {
title: string
subtitle: string
@@ -188,4 +214,3 @@ export interface SiteContent {
hasznalatiFeltetelek: LegalPageContent
}
}
+4 -4
View File
@@ -1,4 +1,4 @@
import { MongoClient, Db } from 'mongodb'
import { MongoClient, Db, Collection, Document } from 'mongodb'
if (!process.env.MONGODB_URI) {
throw new Error('Please add MONGODB_URI to your environment variables')
@@ -37,9 +37,9 @@ export async function getDb(): Promise<Db> {
return client.db(process.env.MONGODB_DB || 'mozdit')
}
export async function getCollection(collectionName: string) {
export async function getCollection<T extends Document = Document>(collectionName: string): Promise<Collection<T>> {
const db = await getDb()
return db.collection(collectionName)
return db.collection<T>(collectionName)
}
// Health check for MongoDB connection
@@ -52,4 +52,4 @@ export async function checkMongoConnection(): Promise<boolean> {
console.error('MongoDB connection check failed:', error)
return false
}
}
}