website refresh: true numbers (56 apps), claims checked against the capability map, ten missing apps, an English twin of every public page (public, choice B), 404 page; site gates 13-18 + 11 decoys; R-902 opened
gates / gates (push) Failing after 13m1s

Part A: claim table documentation/audits/website-refresh-2026-10-08/claims.md; "100% open source" corrected
from the licence read; unbacked claims cut (firewall, RAID, snapshots, new-machine restore, self-managed
mode, household VPN, "never lost"). Part B: dawarich, docmost, grimmory, homebox, karakeep, mealie, metube,
radicale, recipe-importer, sparkyfitness cards; plant-it and wger cut (not offered). Part C: /en/ twins,
nav language switch, hreflang both ways, sitemap with xhtml:link, og-image-en.png. Part D: site_gates.py
twins/lang/same-apps/no-Hungarian/FAQ-JSON-LD/tail. Operator ruling 9 + choice B in 10-localisation.md §11,
§10.7. Register: R-902 (contact mailer source in no repo); R-813, R-784, R-559 annotated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012qRErfCoiTkvDK9N5XHbzb
This commit is contained in:
2026-10-08 08:29:17 +02:00
parent b119301c6f
commit 67b3fd23d1
115 changed files with 9846 additions and 279 deletions
@@ -0,0 +1,241 @@
# -*- coding: utf-8 -*-
"""Part B + C: /alkalmazasok (HU) and /en/apps (EN) from one card list. Starts from HEAD each run."""
import html, os, re, sys
sys.path.insert(0, __file__.rsplit("/", 1)[0])
from common import *
from cards_data import *
src = head_text("alkalmazasok.html")
cats = re.findall(r'(<div class="category-section" id="([^"]+)">\s*<div class="category-header">\s*<span class="category-icon">(.*?)</span>).*?\n </div>\n\n', src, re.S)
icons = {cid: icon for _, cid, icon in cats}
cards = [] # dicts in page order
for m in re.finditer(r'<div class="category-section" id="([^"]+)">(.*?)\n </div>\n\n', src, re.S):
cid, body = m.group(1), m.group(2)
for cm, c in re.findall(r'<!-- (.*?) -->\n\s*<div class="app-card">(.*?)\n </div>\n(?=\s*(?:<!--|</div>))', body, re.S):
if cm in REMOVE:
continue
logo = re.search(r'<img src="/assets/([^"]+)" alt="([^"]*)" class="app-logo"', c).group(1)
shot = re.search(r'<img src="/assets/([^"]+)" alt="[^"]*" class="app-screenshot">', c)
href = re.search(r'<a href="([^"]*)"[^>]*class="github-link"', c).group(1)
lab = re.search(r'alt="GitHub">(.*?)</a>', c).group(1)
d = dict(comment=cm, cat=cid, name=re.search(r"<h3>(.*?)</h3>", c).group(1), logo=logo,
shot=shot.group(1) if shot else None, src=href, srclabel="site" if lab == "Weboldal" else "code",
badge="oss",
hu=(re.search(r'class="app-tagline">(.*?)</span>', c).group(1),
re.search(r'class="app-description">(.*?)</p>', c, re.S).group(1),
re.findall(r'class="app-feature">(.*?)</span>', c)))
d.update(OVERRIDE.get(cm, {}))
cards.append(d)
for after, nc in NEW_CARDS:
i = next(k for k, c in enumerate(cards) if c["comment"] == after)
nc = dict(nc)
nc.setdefault("srclabel", "code")
nc["hu"] = NEW_HU[nc["comment"]]
nc["shot"] = nc["slug"] + "-screenshot-1.webp"
cards.insert(i + 1, nc)
def asset_ok(name):
return name and os.path.exists(os.path.join(W, "assets", name))
for c in cards:
# a logo referenced as .svg that exists only as .png: point at the .png (saves a 404 per load)
if not asset_ok(c["logo"]):
alt = c["logo"].rsplit(".", 1)[0] + ".png"
if asset_ok(alt):
c["logo"] = alt
else:
c["logo_missing"] = True
if not asset_ok(c["shot"]):
c["shot"] = None
c["en"] = EN[c["comment"]]
missing = [c["comment"] for c in cards if c.get("logo_missing")]
noshot = [c["comment"] for c in cards if not c["shot"]]
print("cards:", len(cards), "| logo missing:", missing, "| no screenshot:", noshot)
def card_html(c, lang):
tag, desc, feats = c[lang]
name = c["name"]
out = [" <!-- %s -->" % c["comment"],
' <div class="app-card">',
' <div class="app-card-header">']
if not c.get("logo_missing"):
out.append(' <img src="/assets/%s" alt="%s" class="app-logo">' % (c["logo"], name))
out += [' <div class="app-title-area"><h3>%s</h3><span class="app-tagline">%s</span></div>' % (name, tag),
' </div>',
' <div class="app-card-body">',
' <p class="app-description">%s</p>' % desc,
' <div class="app-features">%s</div>' % "".join('<span class="app-feature">%s</span>' % f for f in feats)]
if c["shot"]:
out.append(' <img src="/assets/%s" alt="%s screenshot" class="app-screenshot">' % (c["shot"], name))
out += [' </div>', ' <div class="app-card-footer">']
if c.get("src"):
out.append(' <a href="%s" target="_blank" rel="noopener" class="github-link"><img src="/assets/github.svg" alt="GitHub">%s</a>'
% (c["src"], SRCLABEL[lang][c["srclabel"]]))
b = c["badge"]
if b == "oss":
out.append(' <span class="open-source-badge"><svg class="ico ico-yes" aria-hidden="true"><use href="/assets/icons.svg?v=1#i-check"/></svg>%s</span>' % BADGE[lang][b])
elif b == "builtin":
out.append(' <span class="open-source-badge"><svg class="ico ico-yes" aria-hidden="true"><use href="/assets/icons.svg?v=1#i-check"/></svg>Open Source · %s</span>' % BADGE[lang][b])
else:
out.append(' <span class="open-source-badge" style="color: var(--warn);"><img src="/assets/checkmark-yellow.svg" alt="">%s</span>' % BADGE[lang][b])
out += [' </div>', ' </div>']
return "\n".join(out)
def sections(lang):
out = []
for n, (cid, icon_name, hu_t, en_id, en_t) in enumerate(CATEGORIES, 1):
cc = [c for c in cards if c["cat"] == cid]
builtin = sum(1 for c in cc if c["badge"] == "builtin")
k = len(cc) - builtin
if lang == "hu":
count = "%d alkalmazás" % k + (" + %d beépített" % builtin if builtin else "")
sec_id, title = cid, hu_t
else:
count = ("%d app" % k if k == 1 else "%d apps" % k) + (" + %d built in" % builtin if builtin else "")
sec_id, title = en_id, en_t
out.append(""" <!-- ==================== -->
<!-- %d. %s -->
<!-- ==================== -->
<div class="category-section" id="%s">
<div class="category-header">
<span class="category-icon">%s</span>
<h2>%s</h2>
<span class="app-count">%s</span>
</div>
<div class="app-grid">
%s
</div>
</div>
""" % (n, html.unescape(title).upper() if lang == "en" else title.upper(), sec_id, icons[cid], title, count,
"\n".join(card_html(c, lang) for c in cc)))
return "\n".join(out)
total = sum(1 for c in cards if c["badge"] != "builtin")
oss = sum(1 for c in cards if c["badge"] == "oss")
assert total == 56, total
print("apps:", total, "| OSI open source:", oss)
# ---------------------------------------------------------------- the Hungarian page
start = src.index(" <!-- ==================== -->\n <!-- 1.")
end = src.index(" </div>\n </section>\n\n <!-- Stats Section -->")
hu = src[:start] + sections("hu") + "\n" + src[end:]
hu = sub1(hu, "<title>Telepíthető alkalmazások — 53 nyílt forráskódú app otthoni szerverre | Felhom.eu</title>",
"<title>Telepíthető alkalmazások — %d app otthoni szerverre | Felhom.eu</title>" % total)
hu = sub1(hu, "Nextcloud, Immich, Jellyfin, Vaultwarden és 53 további nyílt forráskódú alkalmazás otthoni szerveredre.",
"Nextcloud, Immich, Jellyfin, Vaultwarden — összesen %d alkalmazás otthoni szerveredre, szinte mind nyílt forráskódú." % total)
hu = sub1(hu, '<meta property="og:title" content="53 nyílt forráskódú alkalmazás otthoni szerverre">',
'<meta property="og:title" content="%d alkalmazás otthoni szerverre">' % total)
hu = sub1(hu, '<meta name="twitter:title" content="53 nyílt forráskódú alkalmazás otthoni szerverre">',
'<meta name="twitter:title" content="%d alkalmazás otthoni szerverre">' % total)
hu = sub1(hu, '"numberOfItems": 45,', '"numberOfItems": %d,' % total)
hu = sub1(hu, '"description": "Nyílt forráskódú, önhostolt alkalmazások otthoni szerverre",',
'"description": "Önhostolt alkalmazások otthoni szerverre, szinte mind nyílt forráskódú",')
hu = sub1(hu, "<p>Válassz a nyílt forráskódú alkalmazások széles kínálatából. Mindegyik ingyenes, közösség által fejlesztett szoftver, amit a saját szervereden futtathatsz.</p>",
"<p>Válassz %d alkalmazás közül. Szinte mind ingyenes, nyílt forráskódú szoftver, és mind a saját szervereden fut.</p>" % total)
hu = sub1(hu, "<li>Az alkalmazások <strong>nyílt forráskódúak</strong> – bárki ellenőrizheti a működésüket, nincs rejtett funkció</li>",
"<li>Az alkalmazások szinte mind <strong>nyílt forráskódúak</strong> – bárki ellenőrizheti a működésüket. A kivételeket a kártyán jelöljük: <em>Zárt forráskód</em> (Plex, Emby), <em>Korlátozott licenc</em> (a forrás olvasható, de a licenc korlátoz), <em>Felhom saját</em> (a mi fejlesztésünk)</li>")
hu = sub1(hu, "<li>Egyedi igények esetén további alkalmazások is telepíthetők – <a href=\"/#kapcsolat\">kérdezz bátran!</a></li>",
"<li>Ha egy alkalmazás hiányzik, megnézzük, felvehető-e a katalógusba – <a href=\"/kapcsolat\">kérdezz bátran!</a></li>")
hu = sub1(hu, """ <h3>49</h3>
<p>Elérhető alkalmazás</p>""", """ <h3>%d</h3>
<p>Elérhető alkalmazás</p>""" % total)
hu = sub1(hu, """ <h3>100%</h3>
<p>Nyílt forráskódú</p>""", """ <h3>%d</h3>
<p>Nyílt forráskódú</p>""" % oss)
hu = sub1(hu, "<p>Ez csak egy részlet a telepíthető alkalmazásokból. Ha van egy konkrét igényed, keress minket és megtaláljuk a megfelelő megoldást!</p>",
"<p>Ez a teljes katalógus. Ha van egy konkrét igényed, keress minket — megnézzük, felvehető-e a megfelelő alkalmazás.</p>")
hu = set_alternates(hu, "/alkalmazasok", "/en/apps")
hu = set_nav(hu, "hu", "/alkalmazasok", "/en/apps")
write("alkalmazasok.html", hu)
# ---------------------------------------------------------------- the English page
en = src[:start] + sections("en") + "\n" + src[end:]
en = en.replace('<html lang="hu">', '<html lang="en">', 1)
head_old = en[: en.index("<!-- shared assets")]
head_new = """<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Apps to install — %(n)d apps for a home server | Felhom.eu</title>
<meta name="description" content="Nextcloud, Immich, Jellyfin, Vaultwarden — %(n)d apps for your home server in Hungary, almost all of them open source. Alternatives to Google Drive, iCloud Photos and Spotify.">
<meta name="keywords" content="Nextcloud, Immich, Jellyfin, Plex, Sonarr, Radarr, Vaultwarden, open-source apps, self-hosted apps, home server apps, Google Photos alternative, Dropbox alternative">
<meta name="robots" content="index, follow">
<link rel="canonical" href="https://felhom.eu/en/apps">
<meta property="og:type" content="website">
<meta property="og:url" content="https://felhom.eu/en/apps">
<meta property="og:title" content="%(n)d apps for a home server">
<meta property="og:description" content="Nextcloud, Immich, Jellyfin, Vaultwarden and many more. Files, photos, media, documents — all from your own server.">
<meta property="og:image" content="https://felhom.eu/assets/og-image.png">
<meta property="og:locale" content="en_GB">
<meta property="og:site_name" content="Felhom.eu">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="%(n)d apps for a home server">
<meta name="twitter:description" content="Nextcloud, Immich, Jellyfin and many more — from your own server.">
<meta name="twitter:image" content="https://felhom.eu/assets/og-image.png">
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "ItemList",
"name": "Felhom.eu — Apps to install",
"description": "Self-hosted apps for a home server, almost all of them open source",
"numberOfItems": %(n)d,
"itemListElement": [
{"@type": "ListItem", "position": 1, "name": "Nextcloud", "description": "Files and office suite"},
{"@type": "ListItem", "position": 2, "name": "Immich", "description": "Photos and videos (a Google Photos alternative)"},
{"@type": "ListItem", "position": 3, "name": "Jellyfin", "description": "Media player and server"},
{"@type": "ListItem", "position": 4, "name": "Vaultwarden", "description": "Password manager (Bitwarden compatible)"},
{"@type": "ListItem", "position": 5, "name": "Paperless-ngx", "description": "Document management"}
]
}
</script>
""" % {"n": total}
en = head_new + en[len(head_old):]
en = sub1(en, "<h1>Telepíthető <span>Alkalmazások</span></h1>", "<h1>Apps to <span>install</span></h1>")
en = sub1(en, re.search(r"<p>Válassz a nyílt.*?</p>", en).group(0),
"<p>Choose from %d apps. Almost all of them are free, open-source software, and every one runs on your own server.</p>" % total)
en = sub1(en, re.search(r"<h3>(<svg[^>]*><use[^>]*/></svg>) Fontos tudnivalók</h3>", en).group(0),
re.search(r"<h3>(<svg[^>]*><use[^>]*/></svg>) Fontos tudnivalók</h3>", en).group(0).replace("Fontos tudnivalók", "Good to know"))
ul_old = re.search(r'(<div class="disclaimer-banner">.*?<ul>)(.*?)(</ul>)', en, re.S)
en = en[:ul_old.start(2)] + """
<li>Almost all of the apps are <strong>open source</strong> – anyone can check how they work. The exceptions are marked on the card: <em>Closed source</em> (Plex, Emby), <em>Restricted licence</em> (the source can be read, but the licence limits its use), <em>Felhom's own</em> (built by us)</li>
<li>Our service covers <strong>installing</strong> the apps, not developing or selling them</li>
<li>The original developers are responsible for how the apps work and for their updates</li>
<li>If an app is missing, we look at whether it can join the catalog – <a href="/en/contact">just ask!</a></li>
""" + en[ul_old.end(2):]
stats = [("Elérhető alkalmazás", "Apps available"), ("Kategória", "Categories"), ("Nyílt forráskódú", "Open source"),
("Licenc költség", "Licence cost")]
for a, b in stats:
en = sub1(en, "<p>%s</p>" % a, "<p>%s</p>" % b)
en = sub1(en, """ <h3>49</h3>""", """ <h3>%d</h3>""" % total)
en = sub1(en, """ <h3>100%</h3>""", """ <h3>%d</h3>""" % oss)
en = sub1(en, "<h3>0 Ft</h3>", "<h3>0 Ft</h3>")
en = sub1(en, "<h2>Nem találod amit keresel?</h2>", "<h2>Can't find what you are looking for?</h2>")
en = sub1(en, re.search(r"<p>Ez csak egy részlet.*?</p>", en).group(0),
"<p>This is the whole catalog. If you need something specific, get in touch — we will look at whether the right app can join it.</p>")
en = sub1(en, '<a href="/kapcsolat" class="cta-button">Kapcsolatfelvétel →</a>', '<a href="/en/contact" class="cta-button">Get in touch →</a>')
en = sub1(en, 'aria-label="Bezárás"', 'aria-label="Close"')
en = sub1(en, 'aria-label="Előző"', 'aria-label="Previous"')
en = sub1(en, 'aria-label="Következő"', 'aria-label="Next"')
en = sub1(en, "`Ugrás ${i + 1}. képhez`", "`Go to image ${i + 1}`")
en = sub1(en, "Betöltés...", "Loading...")
en = sub1(en, "'Menü bezárása'", "'Close menu'")
en = sub1(en, "'Menü megnyitása'", "'Open menu'")
en = sub1(en, '<body class="page-alkalmazasok">', '<body class="page-alkalmazasok">')
en = set_alternates(en, "/alkalmazasok", "/en/apps")
en = set_nav(en, "en", "/en/apps", "/alkalmazasok")
en = set_footer(en, "en")
write("en/apps.html", en)
print("written: alkalmazasok.html, en/apps.html")
@@ -0,0 +1,45 @@
# -*- coding: utf-8 -*-
"""404.html — nginx's error_page target (manifests/webpage.yaml). Hungarian first, one English line, a link home.
Every asset path is absolute: the page is served at whatever URL was wrong."""
import re, sys
sys.path.insert(0, __file__.rsplit("/", 1)[0])
from common import *
src = read("letoltes.html")
tail = src[src.index(" <footer>"):] # footer + the menu script, identical to the other HU pages
page = """<!DOCTYPE html>
<html lang="hu">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Az oldal nem található | Felhom.eu</title>
<meta name="robots" content="noindex, follow">
<!-- shared assets: bump ?v= together on any site.css/icons.svg change -->
<link rel="stylesheet" href="/assets/site.css?v=2">
<link rel="icon" type="image/png" href="/assets/logo_favicon_2.svg">
<script defer src="https://stats.felhom.eu/script.js" data-website-id="d419db57-5e1c-48df-bdb3-c29f25fb1661"></script>
</head>
<body class="page-404">
%s
<div class="mobile-overlay" id="mobileOverlay"></div>
<main>
<section class="page-header">
<div class="container">
<h1>Ez az oldal <span>nem található</span></h1>
<p>A keresett cím nem létezik, vagy elköltözött. A kezdőlapról mindent elérsz.</p>
<p lang="en">This page does not exist. <a href="/en/">Go to the English home page</a>.</p>
</div>
</section>
<section class="content-section">
<div class="container">
<div class="apps-cta">
<a href="/" class="cta-button">Vissza a kezdőlapra →</a>
</div>
</div>
</section>
</main>
%s""" % (nav("hu", None, "/en/"), tail)
write("404.html", page)
print("written 404.html")
@@ -0,0 +1,25 @@
# -*- coding: utf-8 -*-
"""sitemap.xml — every public page in both languages, each with its xhtml:link alternates (choice B)."""
import sys
sys.path.insert(0, __file__.rsplit("/", 1)[0])
from common import *
META = {"/": ("monthly", "1.0", "0.9"), "/alkalmazasok": ("monthly", "0.8", "0.7"), "/biztonsagimentes": ("monthly", "0.7", "0.6"),
"/technologiak": ("monthly", "0.7", "0.6"), "/gyik": ("monthly", "0.6", "0.5"), "/kapcsolat": ("yearly", "0.5", "0.4"),
"/letoltes": ("monthly", "0.3", "0.3")}
order = ["/", "/alkalmazasok", "/biztonsagimentes", "/technologiak", "/gyik", "/kapcsolat", "/letoltes"]
pairs = {p[1]: p[3] for p in PAIRS}
out = ['<?xml version="1.0" encoding="UTF-8"?>',
'<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml">']
for lang in ("hu", "en"):
for hu in order:
en = pairs[hu]
freq, phu, pen = META[hu]
loc = hu if lang == "hu" else en
out += [" <url>", " <loc>%s%s</loc>" % (SITE, loc),
' <xhtml:link rel="alternate" hreflang="hu" href="%s%s"/>' % (SITE, hu),
' <xhtml:link rel="alternate" hreflang="en" href="%s%s"/>' % (SITE, en),
' <xhtml:link rel="alternate" hreflang="x-default" href="%s%s"/>' % (SITE, hu),
" <changefreq>%s</changefreq>" % freq, " <priority>%s</priority>" % (phu if lang == "hu" else pen), " </url>"]
out.append("</urlset>")
write("sitemap.xml", "\n".join(out))
print("written sitemap.xml:", 2 * len(order), "urls")
@@ -0,0 +1,163 @@
# -*- coding: utf-8 -*-
"""The app cards of /alkalmazasok and /en/apps, one record per card (website refresh 2026-10-08).
Existing Hungarian card text is read from the live page and kept as it is unless OVERRIDE_HU says otherwise.
NEW_HU is the Hungarian for the ten cards added today; it starts from the catalog's own Hungarian
(`description`, `app_info.tagline`, `use_cases`). EN is the English for every card: the catalog's `i18n.en`
text where the card says what the catalog says, otherwise a translation in the same voice.
badge: oss | closed | restricted | own (licences: documentation/audits/licences-2026-10-02/TABLE.md)
"""
# Category order and the new cards' place: (category, insert after <comment>) — None = first.
CATEGORIES = [
# id, icon, HU title, EN id, EN title
("fajlkezeles", "i-folder", "Fájlkezelés & Irodai", "files", "Files & Office"),
("media", "i-clapperboard", "Média & Szórakozás", "media", "Media & Entertainment"),
("otthon", "i-home", "Otthon & Életmód", "home", "Home & Lifestyle"),
("produktivitas", "i-file-text", "Produktivitás & Tudáskezelés", "productivity", "Productivity & Knowledge"),
("rendszer", "i-code", "Rendszer & Fejlesztés", "system", "System & Development"),
]
REMOVE = {"Plant-it", "wger"} # lifecycle abandoned / hidden in the catalog: not offered to a household
# (after_comment, card) — inserted in this order
NEW_CARDS = [
("Komga", dict(comment="Grimmory", name="Grimmory", slug="grimmory", logo="grimmory-logo.svg",
src="https://github.com/grimmory-tools/grimmory", badge="oss", cat="media")),
("Audiobookshelf", dict(comment="MeTube", name="MeTube", slug="metube", logo="metube-logo.svg",
src="https://github.com/alexta69/metube", badge="oss", cat="media")),
("Tandoor Recipes", dict(comment="Mealie", name="Mealie", slug="mealie", logo="mealie-logo.svg",
src="https://github.com/mealie-recipes/mealie", badge="oss", cat="otthon")),
("Mealie", dict(comment="Recipe Importer", name="Recipe Importer", slug="recipe-importer",
logo="recipe-importer-logo.svg", src=None, badge="own", cat="otthon")),
("Wishlist", dict(comment="Homebox", name="Homebox", slug="homebox", logo="homebox-logo.svg",
src="https://github.com/sysadminsmedia/homebox", badge="oss", cat="otthon")),
("Gramps Web", dict(comment="SparkyFitness", name="SparkyFitness", slug="sparkyfitness",
logo="sparkyfitness-logo.svg", src="https://github.com/CodeWithCJ/SparkyFitness",
badge="restricted", cat="otthon")),
("Wanderer", dict(comment="Dawarich", name="Dawarich", slug="dawarich", logo="dawarich-logo.svg",
src="https://github.com/Freika/dawarich", badge="oss", cat="otthon")),
("Vikunja", dict(comment="Radicale", name="Radicale", slug="radicale", logo="radicale-logo.svg",
src="https://github.com/Kozea/Radicale", badge="oss", cat="produktivitas")),
("Outline", dict(comment="Docmost", name="Docmost", slug="docmost", logo="docmost-logo.svg",
src="https://github.com/docmost/docmost", badge="oss", cat="produktivitas")),
("Docmost", dict(comment="Karakeep", name="Karakeep", slug="karakeep", logo="karakeep-logo.svg",
src="https://github.com/karakeep-app/karakeep", badge="oss", cat="produktivitas")),
]
NEW_HU = {
"Grimmory": ("E-könyvtár böngészőben",
"E-könyvek és képregények rendezett könyvtárban, borítóval és adatokkal. Olvasás a böngészőben, a haladás mentésével; Kobo- és KOReader-szinkron, OPDS-katalógus az olvasóalkalmazásoknak. Csak a családtagjaid érik el, saját belépéssel.",
["Böngészős olvasó", "Kobo szinkron", "KOReader", "OPDS"]),
"MeTube": ("Videók letöltése linkből",
"Egy videó vagy lejátszási lista letöltése a linkjéből, videóként vagy csak hangként. A letöltések a meghajtódra kerülnek, a fájlböngészőben és a médialejátszódban is látod őket. Csak a családtagjaid érik el.",
["Videó és hang", "Lejátszási listák", "Saját meghajtóra", "Csak a családnak"]),
"Mealie": ("Receptek és étkezésterv",
"Receptkezelő és étkezéstervező a családnak. Receptek importálása weboldalakról, heti menü és bevásárlólista, megosztás a családtagokkal — magyar felülettel is.",
["Recept import", "Heti menü", "Bevásárlólista", "Magyar felület"]),
"Recipe Importer": ("Magyar receptoldalakról",
"Receptek importálása hat magyar oldalról (mindmegette.hu, streetkitchen.hu, nosalty.hu, sobors.hu, kiskegyed.hu, gastrohobbi.hu) Mealie-be és Tandoorba — egyenként vagy tömegesen, importálás előtti szerkesztéssel.",
["6 magyar oldal", "Tömeges import", "Mealie", "Tandoor"]),
"Homebox": ("Otthoni leltár",
"Tartsd számon a tárgyaidat: helyszínek, címkék és kategóriák szerint rendezve, garancia-lejárattal és beszerzési árral. Vonalkód-szkenneléssel gyorsan bővíthető, és jól jön egy biztosítási kárnál is.",
["Leltár", "Garanciák", "Vonalkód", "Címkék"]),
"SparkyFitness": ("Táplálkozási napló",
"Napi étkezések és kalóriabevitel naplózása, testsúly és testméretek grafikonon, edzések rögzítése. Tápanyagcélok (fehérje, szénhidrát, zsír) beállítása és követése.",
["Kalóriaszámláló", "Testsúly", "Edzések", "Tápanyagcélok"]),
"Dawarich": ("A Google Idővonal helyett",
"Saját helyelőzmények térképen: a telefonod a te szerveredre küldi, hol jártál. Térkép, statisztika és utazások; a régi adataid Google Takeout, GPX vagy GeoJSON fájlból importálhatók.",
["Térkép", "Statisztika", "Takeout import", "Mobil app"]),
"Radicale": ("Naptár és névjegyek",
"A telefonod naptára és névjegyei a saját szervereden, nem a Google-nél vagy az Apple-nél. Közös családi naptár; Androidon a DAVx5, iPhone-on és Macen a beépített naptár szinkronizál vele.",
["CalDAV", "CardDAV", "Családi naptár", "Thunderbird"]),
"Docmost": ("Notion-szerű wiki",
"Modern wiki és dokumentáció platform Notion-szerű blokk szerkesztővel. Terek és oldalak hierarchiában, valós idejű közös szerkesztés, keresés a teljes tartalomban.",
["Blokk szerkesztő", "Közös szerkesztés", "Keresés", "Táblázatok"]),
"Karakeep": ("Kereshető saját archívum",
"Könyvjelzők, cikkek, jegyzetek és képek egy helyen. A Karakeep letölti és elteszi a mentett oldalak tartalmát, akkor is, ha az oldal később eltűnik. Teljes szöveges keresés, böngészőbővítmény és telefonos alkalmazás.",
["Oldalmentés", "Keresés", "Böngészőbővítmény", "Mobil app"]),
}
# Corrections to existing cards (name, link, badge) — by comment.
OVERRIDE = {
"FileBrowser": dict(badge="builtin", hu=("Egyszerű webes fájlkezelő",
"Könnyű és gyors webes fájlkezelő, minden Felhom dobozba beépítve. Böngészd, töltsd fel és oszd meg a fájljaidat bárhonnan, modern felületen keresztül.",
["Drag & drop", "Megosztás", "Előnézet", "Keresés"])),
"Plex": dict(badge="closed", src="https://www.plex.tv", srclabel="site"),
"Emby": dict(badge="closed", src="https://emby.media", srclabel="site"),
"n8n": dict(badge="restricted"),
"Outline": dict(badge="restricted"),
"Tandoor Recipes": dict(badge="restricted"),
# the catalog ships Calibre-Web Automated, not the original Calibre-Web
"Calibre-web": dict(name="Calibre-Web Automated", src="https://github.com/crocodilestick/Calibre-Web-Automated"),
}
BADGE = {
"hu": {"oss": "Open Source", "closed": "Zárt forráskód", "restricted": "Korlátozott licenc",
"own": "Felhom saját", "builtin": "Beépített"},
"en": {"oss": "Open Source", "closed": "Closed source", "restricted": "Restricted licence",
"own": "Felhom's own", "builtin": "Built in"},
}
SRCLABEL = {"hu": {"code": "Forráskód", "site": "Weboldal"}, "en": {"code": "Source code", "site": "Website"}}
EN = {
"Nextcloud": ("Your own Google Drive", "A full cloud service on your own server. File sync, calendar, contacts, document editing and a great many add-ons.", ["File sync", "Calendar", "Documents", "Mobile app"]),
"FileBrowser": ("Simple web file manager", "A light, fast web file manager, built into every Felhom box. Browse, upload and share your files from anywhere, in a modern interface.", ["Drag & drop", "Sharing", "Preview", "Search"]),
"Gokapi": ("Temporary file sharing", "A lightweight file-sharing server with expiry dates and download limits. Good for sharing large files quickly and safely.", ["Expiry date", "Download limit", "Password protection"]),
"Zipline": ("ShareX/Flameshot server", "A modern file and image sharing server with many features. Good for sharing screenshots, shortening links and keeping text snippets.", ["Screenshots", "Link shortener", "Gallery", "API"]),
"OnlyOffice": ("An office suite in the browser", "A full office suite in your browser. Edit documents, spreadsheets and presentations, with very good Microsoft Office compatibility.", ["Documents", "Spreadsheets", "Presentations", "Collaboration"]),
"BentoPDF": ("PDF toolkit (privacy first)", "A privacy-first PDF toolkit with more than 70 tools. Merge, split, convert, compress, OCR, encrypt and sign, all in the browser, with nothing sent to a server.", ["70+ tools", "Client-side", "OCR", "Convert"]),
"Papra": ("A minimal document archive", "A clean, modern document archive. Automatic tags, e-mail import and OCR text recognition: a digital filing cabinet, simple and tidy.", ["Auto tags", "E-mail import", "OCR", "Modern UI"]),
"Immich": ("A Google Photos alternative", "A fast, modern photo and video backup. Automatic upload from your phone, face and object recognition, sharing and memories.", ["Auto backup", "Face recognition", "Map", "Mobile app"]),
"Jellyfin": ("Media server (Netflix)", "A free, open-source media server. Stream films, series and music from anywhere, to any device.", ["Streaming", "Transcoding", "Mobile app", "TV app"]),
"Plex": ("Media server (premium)", "A popular media server with a polished interface. Films, series, music and photos in one place, with very good app support.", ["Streaming", "Plex Pass", "Intro skip", "Watch Together"]),
"Emby": ("Media server", "A personal media server with live TV and DVR support. An organised library, remote access, and sync for offline playback.", ["Streaming", "Live TV", "DVR", "Sync"]),
"Navidrome": ("Music server (Spotify)", "A light, modern music server. Stream your own music collection from anywhere; Subsonic-compatible, with many mobile apps.", ["Streaming", "Subsonic API", "Mobile apps", "Playlists"]),
"Audiobookshelf": ("Audiobooks & podcasts", "An audiobook and podcast server. Details fetched for you, carry on where you stopped, and its own mobile apps.", ["Audiobooks", "Podcasts", "Sync", "Mobile app"]),
"Calibre-web": ("E-book library", "A web interface for your Calibre library. Browse, read and download your e-books from anywhere, with Kindle support.", ["E-books", "Send to Kindle", "OPDS", "Reader"]),
"Komga": ("Comics & manga server", "Manage and read comics, manga and magazines. A modern web reader, OPDS support and popular mobile apps.", ["Web reader", "OPDS", "Metadata", "Tachiyomi"]),
"Sonarr": ("Series manager", "Downloads and organises series for you. Follow your favourite series and new episodes are downloaded automatically.", ["Auto download", "Calendar", "Quality profiles", "Integrations"]),
"Radarr": ("Film manager", "Downloads and organises films for you. Make a wish list and the films arrive in your library on their own.", ["Wish list", "Auto download", "Quality profiles", "Integrations"]),
"Seerr": ("Media request manager", "Ask for films and series the easy way. Family and friends can request titles, and the system downloads them for you.", ["Requests", "Users", "Notifications", "Integrations"]),
"RoMM": ("ROM collection manager", "A good-looking web interface for your retro game ROM collection. Details and cover art fetched for you, sorted by platform, and playable in the browser.", ["ROM library", "Metadata", "EmulatorJS", "IGDB"]),
"Crafty Controller": ("Minecraft server manager", "A modern web manager for Minecraft servers. Run several servers from one interface, with automatic backups, player statistics and full remote access.", ["Multi-server", "Backups", "Console", "Statistics"]),
"Home Assistant": ("Smart home hub", "The world's most popular open-source smart home platform. More than 3000 integrations, local control and automations, with no cloud and full privacy.", ["3000+ integrations", "Automations", "Local control", "Mobile app"]),
"n8n": ("Workflow automation", "Visual workflow automation in the style of Zapier or Make. Connect any service and build automations without code, on your own server.", ["400+ integrations", "Visual editor", "AI features", "Webhooks"]),
"Tandoor Recipes": ("Recipe manager", "Keep your recipes, plan meals and make a shopping list. Import recipes from the web and share them with your family.", ["Recipe import", "Meal planning", "Shopping list", "Sharing"]),
"Wishlist": ("Family wish list", "A wish list you can share, for Christmas, birthdays and other occasions. The family can reserve gifts, so nobody buys the same present twice.", ["Groups", "Reservations", "Link import", "PWA"]),
"Gramps Web": ("Family tree builder", "A web platform for family trees and genealogy. Build your family tree, add photos and documents, and share it with your family safely.", ["Family tree", "GEDCOM", "Collaboration", "Sync"]),
"Actual Budget": ("Budget planner", "Local-first personal finance. Plan a budget, track transactions and set money goals.", ["Budget", "Sync", "Bank import", "Reports"]),
"AdventureLog": ("Travel log", "Record your trips and adventures. A map view, photos and statistics about the places you have been.", ["Map", "Photos", "Statistics", "Sharing"]),
"Wanderer": ("Hike planner", "Plan hikes and routes and keep your GPX files. A map with contour lines, and route statistics.", ["GPX import", "Maps", "Elevation", "Routes"]),
"Cal.com": ("Appointment booking", "An open-source Calendly alternative. Easy appointment booking with clients, calendar integrations and automatic reminders.", ["Booking page", "Calendar sync", "Teams", "Reminders"]),
"Vikunja": ("Task manager (Todoist)", "An open-source Todoist alternative. Tasks, projects and deadlines, in Kanban, Gantt and list views. Reminders and team work.", ["Kanban", "Gantt", "Reminders", "Teams"]),
"Kimai": ("Time tracking", "Professional time tracking for freelancers and teams. Projects, clients, reports and invoice export in one system.", ["Projects", "Reports", "Export", "Invoicing"]),
"Rallly": ("Meeting poll (Doodle)", "Find a time for a meeting or an event, the easy way. A Doodle alternative: people vote without an account, and time zones are handled.", ["Polls", "No sign-up", "Time zones", "Notifications"]),
"Claper": ("Interactive presentations", "Live polls, questions and word clouds for presentations. A Mentimeter and AhaSlides alternative that makes your talks interactive.", ["Live polls", "Questions", "Word cloud", "Reactions"]),
"Ghost": ("Blog & newsletter platform", "A modern publishing platform for bloggers and creators. Good-looking themes, a built-in newsletter, and members and subscriptions.", ["Blog", "Newsletter", "Members", "SEO"]),
"Paperless-ngx": ("A digital filing cabinet", "Scan your paper documents and stop worrying about them. OCR, automatic sorting and full-text search.", ["OCR", "Auto sorting", "Search", "Tags"]),
"BookStack": ("Book-style documentation", "A simple self-hosted wiki. Arrange what you know into books, chapters and pages that are easy to search.", ["WYSIWYG", "Books", "Search", "Permissions"]),
"Outline": ("Team knowledge base", "A good-looking wiki and knowledge base for teams. Markdown, real-time collaboration and structured documentation.", ["Markdown", "Collaboration", "Search", "Integrations"]),
"PrivateBin": ("Encrypted note sharing", "A minimal, encrypted pastebin. The server cannot see what you share, which makes it right for sending sensitive text safely.", ["E2E encryption", "Expiry", "Burn after reading", "Password protection"]),
"Vaultwarden": ("Password manager (Bitwarden)", "A light Bitwarden server. Keep your passwords safe and in sync on every device.", ["E2E encryption", "Mobile app", "Browser extension", "2FA"]),
"Homepage": ("Personal start page", "A good-looking, feature-rich start page for your server. Service status, widgets and bookmarks in one place.", ["Widgets", "Integrations", "Themes", "Status"]),
"Glance": ("Information dashboard", "A minimal, fast dashboard for all kinds of information. RSS, weather, bookmarks, Reddit, YouTube and more on one page.", ["RSS", "Weather", "YouTube", "Fast"]),
"Uptime Kuma": ("Service monitoring", "A neat service monitor. Watch your websites and APIs and get told when something stops working.", ["HTTP/TCP/DNS", "Notifications", "Status page", "Charts"]),
"Grafana": ("Data visualisation", "A professional monitoring and visualisation platform. Good-looking dashboards, alerts and analysis, from any data source.", ["Dashboards", "Alerts", "Data sources", "Queries"]),
"Termix": ("Server manager (SSH)", "A web SSH terminal and server management platform. Connect to your servers from a browser anywhere, manage SSH tunnels and edit files remotely.", ["SSH terminal", "Tunnels", "File manager", "2FA"]),
"Gitea": ("Git server (GitHub)", "Light, fast Git hosting. Your own GitHub or GitLab, with issue tracking, CI/CD and a package registry.", ["Git hosting", "Issues", "CI/CD", "Wiki"]),
"Code-Server": ("VS Code in the browser", "Visual Studio Code in the browser. Work from anywhere, on any device, with the same familiar interface.", ["VS Code", "Extensions", "Terminal", "Git"]),
"OpenGist": ("Code snippets (Gist)", "Your own GitHub Gist server. Share code snippets with syntax highlighting and Git-based version history.", ["Snippets", "Syntax", "Git", "Embed"]),
# the ten new cards — from the catalog's i18n.en
"Grimmory": ("E-book library in the browser", "E-books and comics in a tidy library, with covers and details. Read in the browser with your progress saved; Kobo and KOReader sync, and an OPDS catalog for reader apps. Only your family members reach it, each with their own sign-in.", ["Browser reader", "Kobo sync", "KOReader", "OPDS"]),
"MeTube": ("Download videos from a link", "Download a video or a playlist from its link, as video or audio only. The downloads go to your drive; you see them in the file browser and your media player. Only your family members reach it.", ["Video and audio", "Playlists", "To your drive", "Family only"]),
"Mealie": ("Recipes and meal plans", "A recipe manager and meal planner for the household. Import a recipe from a web page, plan the week's meals and get a shopping list, and share recipes with the family; the interface speaks English and Hungarian among others.", ["Recipe import", "Weekly menu", "Shopping list", "Multilingual"]),
"Recipe Importer": ("From Hungarian recipe sites", "Import recipes from six Hungarian sites (mindmegette.hu, streetkitchen.hu, nosalty.hu, sobors.hu, kiskegyed.hu, gastrohobbi.hu) into Mealie and Tandoor, one at a time or in bulk, and edit them before they are saved.", ["6 Hungarian sites", "Bulk import", "Mealie", "Tandoor"]),
"Homebox": ("Home inventory", "Keep track of what you own: sorted by location, tag and category, with warranties and what you paid. Scan a barcode to add something quickly, and have the list ready for an insurance claim.", ["Inventory", "Warranties", "Barcode", "Tags"]),
"SparkyFitness": ("Food diary", "Log what you eat and how many calories it comes to, track your weight and measurements on a graph, and record workouts. Set nutrition targets (protein, carbohydrate, fat) and follow them.", ["Calorie counter", "Weight", "Workouts", "Nutrition targets"]),
"Dawarich": ("Instead of Google Timeline", "Your own location history on a map: your phone sends where you have been to your own server. A map, statistics and trips; bring your old data from a Google Takeout, GPX or GeoJSON file.", ["Map", "Statistics", "Takeout import", "Mobile app"]),
"Radicale": ("Calendar and contacts", "Your phone calendar and contacts on your own server, not with Google or Apple. A shared family calendar; on Android DAVx5, on iPhone and Mac the built-in calendar syncs with it.", ["CalDAV", "CardDAV", "Family calendar", "Thunderbird"]),
"Docmost": ("A Notion-like wiki", "A modern wiki and documentation platform with a Notion-like block editor. Spaces and pages in a tree, several people editing at once, and search across all of it.", ["Block editor", "Co-editing", "Search", "Tables"]),
"Karakeep": ("Your own searchable archive", "Bookmarks, articles, notes and images in one place. Karakeep downloads and keeps the content of the pages you save, even if a page disappears later. Full-text search, a browser extension and a phone app.", ["Page saving", "Search", "Browser extension", "Mobile app"]),
}
@@ -0,0 +1,120 @@
# -*- coding: utf-8 -*-
"""Shared pieces of the website refresh 2026-10-08: the page map, the two navs, hreflang, file IO."""
import io, os, re, subprocess
REPO = "/mnt/5_hdd/felhom.eu/git/felhom.eu"
W = os.path.join(REPO, "website")
SITE = "https://felhom.eu"
# hu file, hu url, en file, en url, hu nav href that is "active", en nav href that is "active"
PAIRS = [
("index.html", "/", "en/index.html", "/en/", None, None),
("alkalmazasok.html", "/alkalmazasok", "en/apps.html", "/en/apps", "/alkalmazasok", "/en/apps"),
("technologiak.html", "/technologiak", "en/technology.html", "/en/technology", "/technologiak", "/en/technology"),
("biztonsagimentes.html", "/biztonsagimentes", "en/backups.html", "/en/backups", "/biztonsagimentes", "/en/backups"),
("gyik.html", "/gyik", "en/faq.html", "/en/faq", "/gyik", "/en/faq"),
("kapcsolat.html", "/kapcsolat", "en/contact.html", "/en/contact", "/kapcsolat", "/en/contact"),
("letoltes.html", "/letoltes", "en/download.html", "/en/download", None, None),
]
HU_ITEMS = [("/#szolgaltatasok", "Szolgáltatások"), ("/alkalmazasok", "Alkalmazások"), ("/technologiak", "Technológiák"),
("/biztonsagimentes", "Mentések"), ("/gyik", "GYIK"), ("/kapcsolat", "Kapcsolat")]
EN_ITEMS = [("/en/#services", "Services"), ("/en/apps", "Apps"), ("/en/technology", "Technology"),
("/en/backups", "Backups"), ("/en/faq", "FAQ"), ("/en/contact", "Contact")]
def nav(lang, active, twin_url):
items = HU_ITEMS if lang == "hu" else EN_ITEMS
lis = []
for href, label in items:
act = ' class="active"' if href == active else ""
lis.append(' <li><a href="%s"%s>%s</a></li>' % (href, act, label))
if lang == "hu":
lis.append(' <li><a href="%s" lang="en" hreflang="en" class="lang-switch">English</a></li>' % twin_url)
else:
lis.append(' <li><a href="%s" lang="hu" hreflang="hu" class="lang-switch">Magyar</a></li>' % twin_url)
home = "/" if lang == "hu" else "/en/"
open_l = "Menü megnyitása" if lang == "hu" else "Open menu"
return (""" <nav>
<div class="container">
<a href="%s" class="logo">
<img src="/assets/logo.png" alt="Felhom.eu logo">
<span class="logo-text">felhom.eu</span>
</a>
<ul class="nav-links">
%s
</ul>
<button class="hamburger" id="hamburger" aria-label="%s" aria-expanded="false">
<span class="hamburger-box">
<span class="hamburger-line"></span>
<span class="hamburger-line"></span>
<span class="hamburger-line"></span>
</span>
</button>
</div>
</nav>""" % (home, "\n".join(lis), open_l))
FOOTER_HU = """ <footer>
<div class="container">
<p>© 2026 felhom.eu — Saját felhőd, saját szabályaid</p>
</div>
</footer>"""
FOOTER_EN = """ <footer>
<div class="container">
<p>© 2026 felhom.eu — Your own cloud, your own rules. Felhom serves households in Hungary.</p>
</div>
</footer>"""
def alternates(hu_url, en_url):
return (' <link rel="alternate" hreflang="hu" href="%s%s">\n'
' <link rel="alternate" hreflang="en" href="%s%s">\n'
' <link rel="alternate" hreflang="x-default" href="%s%s">') % (SITE, hu_url, SITE, en_url, SITE, hu_url)
def head_text(path):
"""The file as it is at HEAD (the source every build starts from, so a re-run is idempotent)."""
rel = os.path.relpath(os.path.join(W, path), REPO)
out = subprocess.run(["git", "-C", REPO, "show", "HEAD:" + rel], capture_output=True)
if out.returncode != 0:
return None
return out.stdout.decode("utf-8-sig")
def read(path):
return io.open(os.path.join(W, path), encoding="utf-8-sig").read()
def write(path, text):
full = os.path.join(W, path)
os.makedirs(os.path.dirname(full), exist_ok=True)
with io.open(full, "w", encoding="utf-8-sig", newline="\n") as f:
f.write(text)
def sub1(s, old, new, count=1):
n = s.count(old)
if n != count:
raise SystemExit("expected %d x %r, found %d" % (count, old[:90], n))
return s.replace(old, new)
def set_nav(s, lang, active, twin_url):
s2, n = re.subn(r" <nav>.*?</nav>", lambda m: nav(lang, active, twin_url), s, count=1, flags=re.S)
assert n == 1
return s2
def set_footer(s, lang):
s2, n = re.subn(r" <footer>.*?</footer>", lambda m: FOOTER_HU if lang == "hu" else FOOTER_EN, s, count=1, flags=re.S)
assert n == 1
return s2
def set_alternates(s, hu_url, en_url):
"""Insert (or replace) the three hreflang links right after the canonical link."""
s = re.sub(r' <link rel="alternate" hreflang="[^"]+" href="[^"]+">\n', "", s)
m = re.search(r' <link rel="canonical" href="[^"]+">\n', s)
assert m, "no canonical"
return s[:m.end()] + alternates(hu_url, en_url) + "\n" + s[m.end():]
@@ -0,0 +1,114 @@
# -*- coding: utf-8 -*-
"""/en/backups — the English twin of biztonsagimentes.html."""
import sys
sys.path.insert(0, __file__.rsplit("/", 1)[0])
from common import *
from en_common import *
s = en_head(read("biztonsagimentes.html"), "/biztonsagimentes", "/en/backups")
s = tr(s, [
("<title>Biztonsági mentés — 3-2-1 mentési stratégia otthoni szerverekhez | Felhom.eu</title>",
"<title>Backups — a 3-2-1 backup strategy for home servers | Felhom.eu</title>"),
('content="Professzionális 3-2-1 biztonsági mentés otthoni szerverhez. Automatikus backup három szinten: a dobozon, egy második meghajtón és titkosítva egy távoli tárhelyen."',
'content="Professional 3-2-1 backups for a home server in Hungary. Automatic backups on three levels: on the box, on a second drive, and encrypted on remote storage."'),
('content="biztonsági mentés, backup, 3-2-1 mentési szabály, offsite backup, adatmentés, otthoni szerver backup, titkosított mentés, automatikus mentés"',
'content="backup, 3-2-1 backup rule, off-site backup, data backup, home server backup, encrypted backup, automatic backup"'),
('content="3-2-1 biztonsági mentés otthoni szerverhez"', 'content="3-2-1 backups for a home server"', 2),
('content="Professzionális szintű adatbiztonság otthon. Automatikus backup, és egy titkosított másolat távol is."',
'content="Professional-grade data safety at home. Automatic backups, and an encrypted copy far away too."'),
('content="Professzionális adatvédelem otthoni szerveredre. Automatikus backup, offsite mentés."',
'content="Professional data protection for your home server. Automatic backups, off-site copies."'),
('"headline": "3-2-1 biztonsági mentési stratégia otthoni szerverekhez",', '"headline": "A 3-2-1 backup strategy for home servers",'),
('"description": "Hogyan védd meg az adataidat professzionális mentési stratégiával otthoni környezetben.",',
'"description": "How to protect your data at home with a professional backup strategy.",'),
("<h1>Biztonsági <span>Mentés</span></h1>", "<h1><span>Backups</span></h1>"),
("<p>Az adataid biztonsága az első. Professzionális mentési stratégiával gondoskodunk róla, hogy egy hiba vagy baleset után is legyen honnan visszahozni.</p>",
"<p>Your data's safety comes first. With a professional backup strategy, we make sure there is something to bring back from after a fault or an accident.</p>"),
("Miért fontos ez?</h3>", "Why does it matter?</h3>"),
("<p>Amikor valaki felhőszolgáltatást választ – legyen az Google Drive, iCloud, vagy bármilyen más – az egyik legfontosabb szempont az <strong>adatbiztonság</strong>. Nem az a kérdés, hogy valaki hozzáfér-e az adatainkhoz, hanem hogy <strong>megmaradnak-e</strong>.</p>",
"<p>When someone picks a cloud service – Google Drive, iCloud or any other – one of the most important things is <strong>data safety</strong>. The question is not whether someone can get at our data, but whether it <strong>survives</strong>.</p>"),
("<p>A nagy szolgáltatók ezt milliárd dolláros infrastruktúrával oldják meg: több adatközpont, redundáns tárolás, automatikus mentések. De vajon otthoni szerveren is elérhető ugyanez a biztonság?</p>",
"<p>The big providers solve this with billion-dollar infrastructure: several data centres, redundant storage, automatic backups. But can a home server be just as safe?</p>"),
("<p><strong>A válasz: igen.</strong> A megfelelő mentési stratégiával az otthoni szervered ugyanolyan biztonságos lehet, mint bármely felhőszolgáltató – sőt, te irányítasz mindent.</p>",
"<p><strong>The answer: yes.</strong> With the right backup strategy, your home server can be as safe as any cloud provider – and you are in charge of everything.</p>"),
("<h2>A 3-2-1 <span>Mentési Szabály</span></h2>", "<h2>The 3-2-1 <span>backup rule</span></h2>"),
("<p>Az iparági szabvány, amit a professzionális rendszergazdák és vállalatok használnak világszerte</p>", "<p>The industry standard that professional system administrators and companies use around the world</p>"),
("<h3>Három másolat az adataidból</h3>", "<h3>Three copies of your data</h3>"),
("<p>Mindig legyen legalább három példány minden fontos adatodból: az eredeti, és két biztonsági másolat. Ha az egyik sérül vagy elvész, még mindig van kettő tartalékban.</p>",
"<p>Always keep at least three copies of everything important: the original and two backups. If one is damaged or lost, there are still two in reserve.</p>"),
('<div class="label">Példa</div>', '<div class="label">Example</div>', 3),
("<p>A családi fotóid megvannak a szerveren (1), egy külső merevlemezen (2), és egy távoli helyen (3).</p>",
"<p>Your family photos are on the server (1), on an external hard drive (2), and in a remote place (3).</p>"),
("<h3>Két különböző tárolótípus</h3>", "<h3>Two different kinds of storage</h3>"),
("<p>Az adatokat legalább kétféle tárolóeszközön tartsd. A különböző eszközök különböző meghibásodási módokkal rendelkeznek – ha az egyik típus elromlik, a másik valószínűleg rendben lesz.</p>",
"<p>Keep your data on at least two kinds of storage device. Different devices fail in different ways – if one kind breaks, the other is probably fine.</p>"),
("<p>A szerver belső SSD-jén tárolod az adatokat, de a mentés külső HDD-re vagy NAS-ra kerül.</p>",
"<p>You keep your data on the server's internal SSD, and the backup goes to an external HDD or a NAS.</p>"),
("<h3>Egy offsite (távoli) másolat</h3>", "<h3>One off-site (remote) copy</h3>"),
("<p>Legalább egy másolat legyen fizikailag más helyen. Ez véd a lokális katasztrófák ellen: tűz, vízkár, betörés, vagy akár egy villámcsapás sem veszélyeztetheti az összes adatodat.</p>",
"<p>Keep at least one copy in a different physical place. That protects against local disasters: a fire, a flood, a burglary or even a lightning strike cannot put all your data at risk.</p>"),
("<p>A mentés automatikusan átmásolódik egy barát/rokon szerverére, vagy titkosítva felhőbe kerül.</p>",
"<p>The backup is copied on its own to a friend's or relative's server, or goes encrypted into the cloud.</p>"),
("<h3>Így néz ki a gyakorlatban</h3>", "<h3>What it looks like in practice</h3>"),
('<div class="label">Szerver</div>', '<div class="label">Server</div>'),
('<div class="sublabel">Eredeti adat</div>', '<div class="sublabel">Original data</div>'),
('<div class="label">Helyi backup</div>', '<div class="label">Local backup</div>'),
('<div class="sublabel">Külső HDD/NAS</div>', '<div class="sublabel">External HDD/NAS</div>'),
('<div class="label">Offsite backup</div>', '<div class="label">Off-site backup</div>'),
('<div class="sublabel">Távoli hely</div>', '<div class="sublabel">Remote place</div>'),
("<h2>Offsite <span>Mentési Megoldások</span></h2>", "<h2>Off-site <span>backup options</span></h2>"),
('<p>Az "1" a 3-2-1 szabályban – a legfontosabb, és sokszor a legelhanyagoltabb</p>', '<p>The "1" in the 3-2-1 rule – the most important, and often the most neglected</p>'),
("<h3>„Haver rendszer\"</h3>", "<h3>The \"buddy system\"</h3>"),
("<p>A legegyszerűbb és legolcsóbb megoldás: cserélj mentéseket egy megbízható ismerőssel. Te mentesz az ő szerverére, ő ment a tiédre.</p>",
"<p>The simplest and cheapest option: swap backups with someone you trust. You back up to their server, they back up to yours.</p>"),
("<li>Nincs havi díj</li>", "<li>No monthly fee</li>"),
("<li>Teljes kontroll az adatok felett</li>", "<li>Full control over the data</li>"),
("<li>Kölcsönös biztonság</li>", "<li>Safety for both of you</li>"),
("<li>End-to-end titkosítás lehetséges</li>", "<li>End-to-end encryption is possible</li>"),
('<div class="label">Hogyan működik?</div>', '<div class="label">How does it work?</div>'),
("<p>Titkosított mentések automatikusan szinkronizálódnak VPN kapcsolaton keresztül.</p>", "<p>Encrypted backups sync on their own over a VPN connection.</p>"),
("<h3>Otthon ↔ Iroda</h3>", "<h3>Home ↔ Office</h3>"),
("<p>Ha van munkahelyed vagy saját irodád, használd ki! A két helyszín természetes földrajzi távolságot biztosít.</p>",
"<p>If you have a workplace or your own office, use it! Two sites give you natural distance.</p>"),
("<li>Meglévő infrastruktúra használata</li>", "<li>Uses what is already there</li>"),
("<li>Biztonságos, ismert környezet</li>", "<li>A safe, familiar place</li>"),
("<li>Gyors helyreállítás szükség esetén</li>", "<li>Quick to recover from when needed</li>"),
('<div class="label">Tipp</div>', '<div class="label">Tip</div>'),
("<p>Egy kis, csendes mini PC az iroda sarkában tökéletes backup célpont lehet.</p>", "<p>A small, quiet mini PC in a corner of the office can be a perfect backup target.</p>"),
("<h3>Titkosított felhő backup</h3>", "<h3>Encrypted cloud backup</h3>"),
("<p>Ha nincs megbízható ismerős vagy második helyszín, a felhő a megoldás. Fontos: az adatokat a feltöltés előtt titkosítjuk!</p>",
"<p>If there is no trusted friend or second site, the cloud is the answer. Important: the data is encrypted before it is uploaded!</p>"),
("<li>Professzionális infrastruktúra</li>", "<li>Professional infrastructure</li>"),
("<li>Földrajzilag elosztott tárolás</li>", "<li>Storage spread across places</li>"),
("<li>Nincs szükség hardverre</li>", "<li>No hardware needed</li>"),
("<li>Havi/éves díj (tárolás méretétől függően)</li>", "<li>A monthly or yearly fee (depending on how much is stored)</li>"),
('<div class="label">Megjegyzés</div>', '<div class="label">Note</div>'),
("<p>A tárhely díja a mentett adatmennyiségtől függ. A Felhom dobozon ezt a megoldást használjuk.</p>",
"<p>The storage fee depends on how much data is backed up. This is the option a Felhom box uses.</p>"),
("<h2>Mi Hogyan <span>Oldjuk Meg?</span></h2>", "<h2>How <span>we do it</span></h2>"),
("<p>Minden általunk telepített szerveren alapértelmezetten beállítjuk a mentéseket</p>", "<p>Backups are set up by default on every server we install</p>"),
("Automatizált mentési rendszer</h3>", "Automatic backups</h3>"),
("<p>A szerver minden éjjel magától készít mentéseket, három szinten: <strong>a dobozon</strong>, egy <strong>második meghajtón</strong> (ha van), és <strong>titkosítva egy távoli tárhelyen</strong>. Nem kell semmire sem emlékezned – a rendszer gondoskodik róla.</p>",
"<p>Every night the server makes backups by itself, on three levels: <strong>on the box</strong>, on a <strong>second drive</strong> (if there is one), and <strong>encrypted on remote storage</strong>. You do not have to remember anything – the system takes care of it.</p>"),
("<p>A mentések inkrementálisak, ami azt jelenti, hogy csak a változások kerülnek mentésre. Ez helyet és időt takarít meg, miközben a teljes visszaállítási lehetőség megmarad.</p>",
"<p>The backups are incremental: only the changes are saved. That saves space and time, and a full restore is still there when you need it.</p>"),
("<strong>Nem csak az adatok:</strong> a Proxmox alapnak köszönhetően nem csupán a fájljaid és adatbázisaid menthetők, hanem az <strong>egész rendszered</strong> is — a teljes doboz is biztonsági mentésbe kerül, és ebből visszaállítható. Ha maga a doboz megy tönkre, az újratelepített rendszerre a titkosított távoli mentésből visszahozhatók az alkalmazásaid adatai — a helyreállítási kóddal.</p>",
"<strong>Not only the data:</strong> thanks to the Proxmox base, not only your files and databases are backed up but your <strong>whole system</strong> too — the whole box goes into a backup, and it can be restored from it. If the box itself breaks, your apps' data can be brought back from the encrypted remote backup onto the reinstalled system — with the recovery code.</p>"),
("<p><strong>A távoli mentés helye:</strong> a Felhom dobozon a távoli másolat egy általunk biztosított tárhelyre kerül, titkosítva. A „haver rendszer” és az iroda általános lehetőségek a 3-2-1 szabályhoz — ezeket ma nem állítjuk be.</p>",
"<p><strong>Where the remote backup goes:</strong> on a Felhom box the remote copy goes, encrypted, to storage we provide. The \"buddy system\" and the office are general options for the 3-2-1 rule — we do not set them up today.</p>"),
("A helyreállítási kód — amit csak te ismersz</h3>", "The recovery code — only you know it</h3>"),
("<p>A távoli mentésed titkosított, és a titkosítás kulcsa egy <strong>helyreállítási kód</strong>. Ez a kód pontosan egyszer jelenik meg: a te képernyődön, a beállításkor. Nálunk nem marad belőle másolat.</p>",
"<p>Your remote backup is encrypted, and the key to the encryption is a <strong>recovery code</strong>. The code appears exactly once: on your screen, when it is set up. We keep no copy of it.</p>"),
("<p>Ez tudatos döntés, és két oldala van. Az egyik, hogy a távoli mentésedet rajtad kívül <strong>senki</strong> nem tudja elolvasni — mi sem. A másik, hogy ha a doboz megsemmisül és a kód is elveszett, akkor a távoli mentés <strong>nekünk sem visszaállítható</strong>. Ezért a kód papírra kerül, és jó helyre.</p>",
"<p>This is a deliberate choice, and it has two sides. One: <strong>nobody</strong> but you can read your remote backup — not even us. The other: if the box is destroyed and the code is lost too, then <strong>we cannot restore</strong> the remote backup either. So the code goes on paper, and somewhere safe.</p>"),
("Monitoring és értesítések</h3>", "Monitoring and notifications</h3>"),
("<p>A mentéseket folyamatosan figyeljük. Ha probléma van – sikertelen mentés, betelő tárhely, kapcsolati hiba – az a felügyeleti rendszerünkben megjelenik, és e-mail értesítés is indul róla. A zárt teszt egyik célja épp az, hogy ezeket az értesítéseket éles körülmények között csiszoljuk.</p>",
"<p>We watch the backups all the time. If there is a problem – a failed backup, storage filling up, a connection fault – it shows up in our monitoring, and an e-mail goes out about it. One aim of the closed test is to polish these notifications in real use.</p>"),
("<h2>Kérdésed van a <span>mentésekről?</span></h2>", "<h2>A question about <span>backups?</span></h2>"),
("<p>Beszéljük át, mi a legjobb megoldás a te helyzetedben. Segítünk megtalálni az egyensúlyt a biztonság és a költségek között.</p>",
"<p>Let's talk about what is best in your situation. We help you find the balance between safety and cost.</p>"),
('<a href="/kapcsolat" class="cta-button">Kapcsolatfelvétel →</a>', '<a href="/en/contact" class="cta-button">Get in touch →</a>'),
])
s = finish_en(s, "/biztonsagimentes", "/en/backups", "/en/backups")
write("en/backups.html", s)
print("written en/backups.html")
@@ -0,0 +1,29 @@
# -*- coding: utf-8 -*-
"""Finishing steps every English twin shares: nav (EN set + switch back), footer, hreflang, menu labels."""
from common import *
def finish_en(s, hu_url, en_url, active):
s = s.replace("'Menü bezárása'", "'Close menu'").replace("'Menü megnyitása'", "'Open menu'")
s = set_alternates(s, hu_url, en_url)
s = set_nav(s, "en", active, hu_url)
s = set_footer(s, "en")
return s
def en_head(s, hu_url, en_url):
"""The language-neutral head swaps: lang, canonical, og:url, mainEntityOfPage, og image, locale."""
s = sub1(s, '<html lang="hu">', '<html lang="en">')
s = sub1(s, '<link rel="canonical" href="%s%s">' % (SITE, hu_url), '<link rel="canonical" href="%s%s">' % (SITE, en_url))
s = sub1(s, '<meta property="og:url" content="%s%s">' % (SITE, hu_url), '<meta property="og:url" content="%s%s">' % (SITE, en_url))
s = s.replace('"mainEntityOfPage": "%s%s"' % (SITE, hu_url), '"mainEntityOfPage": "%s%s"' % (SITE, en_url))
s = s.replace("https://felhom.eu/assets/og-image.png", "https://felhom.eu/assets/og-image-en.png")
s = sub1(s, '<meta property="og:locale" content="hu_HU">', '<meta property="og:locale" content="en_GB">')
return s
def tr(s, pairs):
for p in pairs:
old, new = p[0], p[1]
s = sub1(s, old, new, p[2] if len(p) > 2 else 1)
return s
@@ -0,0 +1,95 @@
# -*- coding: utf-8 -*-
"""/en/contact — the English twin of kapcsolat.html. Same form, same /api/contact, same ?tema= values;
English subject labels go into the mail subject. The consent text is translated as it is (R-813 owns it)."""
import sys
sys.path.insert(0, __file__.rsplit("/", 1)[0])
from common import *
from en_common import *
s = en_head(read("kapcsolat.html"), "/kapcsolat", "/en/contact")
s = tr(s, [
("<title>Kapcsolat — Kérj személyre szabott ajánlatot otthoni szerveredhez | Felhom.eu</title>",
"<title>Contact — ask for a personal offer for your home server | Felhom.eu</title>"),
('content="Írj nekünk és személyre szabott ajánlatot készítünk otthoni szerveredhez. Kérdésed van? Segítünk! Email: info@felhom.eu"',
'content="Write to us and we will make a personal offer for your home server in Hungary. A question? We help! E-mail: info@felhom.eu"'),
('content="kapcsolat, ajánlatkérés, otthoni szerver ajánlat, Felhom elérhetőség"', 'content="contact, request an offer, home server offer, Felhom contact"'),
('content="Kapcsolat — Felhom.eu"', 'content="Contact — Felhom.eu"', 2),
('content="Írj nekünk és személyre szabott ajánlatot készítünk otthoni szerveredhez."', 'content="Write to us and we will make a personal offer for your home server."'),
('content="Kérdésed van? Írj nekünk: info@felhom.eu"', 'content="A question? Write to us: info@felhom.eu"'),
("<h1>Írj nekünk <span>bátran!</span></h1>", "<h1>Write to <span>us!</span></h1>"),
("<p>Kérdésed van, vagy szeretnéd megbeszélni az igényeidet? Töltsd ki az űrlapot, és hamarosan válaszolunk.</p>",
"<p>A question, or want to talk about what you need? Fill in the form and we will answer soon. We answer in English or Hungarian.</p>"),
('<label for="name">Név <span class="required">*</span></label>', '<label for="name">Name <span class="required">*</span></label>'),
('placeholder="Teljes neved"', 'placeholder="Your full name"'),
('<div class="error-message" id="nameError">Kérlek, add meg a neved.</div>', '<div class="error-message" id="nameError">Enter your name.</div>'),
('<label for="email">Email <span class="required">*</span></label>', '<label for="email">E-mail <span class="required">*</span></label>'),
('placeholder="pelda@email.hu"', 'placeholder="you@example.com"'),
('<div class="error-message" id="emailError">Kérlek, adj meg egy érvényes email címet.</div>', '<div class="error-message" id="emailError">Enter a valid e-mail address.</div>'),
('<label for="subject">Tárgy <span class="required">*</span></label>', '<label for="subject">Subject <span class="required">*</span></label>'),
('<option value="" disabled selected>Válassz témát...</option>', '<option value="" disabled selected>Choose a topic...</option>'),
('<option value="zart-teszt">Zárt teszt — jelentkezem tesztelőnek</option>', '<option value="zart-teszt">Closed test — I want to be a tester</option>'),
('<option value="erdeklodes">Érdeklődés — szeretném megismerni a szolgáltatást</option>', '<option value="erdeklodes">Interest — I would like to learn about the service</option>'),
('<option value="arajanlat">Árajánlat kérés — konkrét igényeim vannak</option>', '<option value="arajanlat">Request a quote — I have specific needs</option>'),
('<option value="tamogatas">Támogatás — meglévő szerveremet érintő kérdés</option>', '<option value="tamogatas">Support — a question about my existing server</option>'),
('<option value="egyeb">Egyéb</option>', '<option value="egyeb">Other</option>'),
('<div class="error-message" id="subjectError">Kérlek, válassz egy témát.</div>', '<div class="error-message" id="subjectError">Choose a topic.</div>'),
('<label for="message">Üzenet <span class="required">*</span></label>', '<label for="message">Message <span class="required">*</span></label>'),
('placeholder="Írd le, miben segíthetünk..."', 'placeholder="Tell us how we can help..."'),
('<div class="error-message" id="messageError">Kérlek, írd le az üzeneted.</div>', '<div class="error-message" id="messageError">Write your message.</div>'),
('<label>Fájl csatolás <span style="color: var(--text-3); font-weight: 400;">(opcionális)</span></label>', '<label>Attach files <span style="color: var(--text-3); font-weight: 400;">(optional)</span></label>'),
("<strong>Kattints ide</strong> vagy húzd ide a fájlokat", "<strong>Click here</strong> or drag your files here"),
('<div class="upload-hint">PDF, képek, dokumentumok · Max. 20 MB összesen</div>', '<div class="upload-hint">PDF, images, documents · max. 20 MB in total</div>'),
("Elfogadom, hogy az űrlapon megadott adataimat a Felhom.eu a megkeresésem megválaszolásához felhasználja. Az adatokat harmadik félnek nem adjuk ki.",
"I agree that Felhom.eu uses the details I give in this form to answer my enquiry. The details are not passed on to third parties."),
('>Az adatkezelési hozzájárulás szükséges.</div>', '>Your consent to the use of your details is needed.</div>'),
('<span class="btn-text">Üzenet küldése →</span>', '<span class="btn-text">Send message →</span>'),
("Közvetlen email</h3>", "E-mail us directly</h3>"),
("<p>Ha inkább emailben írnál:</p>", "<p>If you would rather send an e-mail:</p>"),
("Válaszidő</h3>", "Reply time</h3>"),
('<p>Általában <strong style="color: var(--blue-bright);">24 órán belül</strong> válaszolunk munkanapokon.</p>',
'<p>We usually answer <strong style="color: var(--blue-bright);">within 24 hours</strong> on working days.</p>'),
("<p>Sürgős támogatási kérdés esetén jelezd a tárgyban.</p>", "<p>If a support question is urgent, say so in the subject.</p>"),
("Miben segíthetünk?</h3>", "How can we help?</h3>"),
("Szerver konfiguráció tanácsadás</li>", "Advice on a server set-up</li>"),
("Alkalmazás telepítés és beállítás</li>", "Installing and setting up apps</li>"),
("Biztonsági mentés kialakítás</li>", "Setting up backups</li>"),
("Távoli elérés beállítása</li>", "Setting up remote access</li>"),
("Meglévő szerver karbantartás</li>", "Maintaining an existing server</li>"),
# --- the form script: every message the visitor can see
("// Preselect the subject from ?tema= (the „Zárt teszt\" CTA on the landing page\n // links here as /kapcsolat?tema=zart-teszt). Unknown values are ignored.",
"// Preselect the subject from ?tema= (the closed-test CTA on the landing page\n // links here as /en/contact?tema=zart-teszt — the same values as the Hungarian page). Unknown values are ignored."),
("console.warn('[kapcsolat] unknown ?tema= value, ignoring:', wanted);", "console.warn('[contact] unknown ?tema= value, ignoring:', wanted);"),
('showStatus(`A "${file.name}" fájltípus nem engedélyezett.`, \'error-state\');', 'showStatus(`The file type of "${file.name}" is not allowed.`, \'error-state\');'),
('showStatus(`A "${file.name}" túl nagy (max. ${formatFileSize(CONFIG.maxSingleFileSize)} / fájl).`, \'error-state\');',
'showStatus(`"${file.name}" is too large (max. ${formatFileSize(CONFIG.maxSingleFileSize)} per file).`, \'error-state\');'),
("showStatus(`Maximum ${CONFIG.maxFiles} fájl csatolható.`, 'error-state');", "showStatus(`You can attach at most ${CONFIG.maxFiles} files.`, 'error-state');"),
("showStatus(`Összesített fájlméret meghaladja a ${formatFileSize(CONFIG.maxTotalFileSize)} limitet.`, 'error-state');",
"showStatus(`The files together are over the ${formatFileSize(CONFIG.maxTotalFileSize)} limit.`, 'error-state');"),
('title="Eltávolítás"', 'title="Remove"'),
("missing.push('név');", "missing.push('name');"),
("missing.push('tárgy');", "missing.push('subject');"),
("missing.push('üzenet');", "missing.push('message');"),
("missing.push('adatkezelési hozzájárulás');", "missing.push('consent');"),
("missing[0] === 'adatkezelési hozzájárulás'", "missing[0] === 'consent'"),
("showStatus('Kérlek, fogadd el az adatkezelési hozzájárulást a küldéshez.', 'error-state');", "showStatus('Tick the consent box to send the form.', 'error-state');"),
("showStatus('Kérlek, töltsd ki az összes kötelező mezőt.', 'error-state');", "showStatus('Fill in every required field.', 'error-state');"),
("'zart-teszt': 'Jelentkezés — zárt teszt',", "'zart-teszt': 'Application — closed test (EN)',"),
("erdeklodes: 'Érdeklődés',", "erdeklodes: 'Interest (EN)',"),
("arajanlat: 'Árajánlat kérés',", "arajanlat: 'Quote request (EN)',"),
("tamogatas: 'Támogatás',", "tamogatas: 'Support (EN)',"),
("egyeb: 'Egyéb',", "egyeb: 'Other (EN)',"),
("showStatus('Üzeneted sikeresen elküldtük! Hamarosan válaszolunk.', 'success');", "showStatus('Your message has been sent. We will answer soon.', 'success');"),
("`Név: ${formData.name}`,", "`Name: ${formData.name}`,"),
("showStatus('Az email kliens megnyílt. A csatolt fájlokat kézzel kell hozzáadnod az emailhez, mert a mailto: link nem támogatja a mellékleteket.', 'error-state');",
"showStatus('Your e-mail program has opened. Add the attached files to the e-mail by hand: a mailto: link cannot carry attachments.', 'error-state');"),
("showStatus('Az email kliens megnyílt az üzeneteddel. Küld el az emailt a kapcsolatfelvételhez!', 'success');",
"showStatus('Your e-mail program has opened with your message. Send the e-mail to get in touch.', 'success');"),
("showStatus(`Hiba történt a küldés során. Kérlek, próbáld újra vagy írj közvetlenül az info@felhom.eu címre.`, 'error-state');",
"showStatus(`Something went wrong while sending. Try again, or write to info@felhom.eu directly.`, 'error-state');"),
("// showSuccess: () => showStatus('Üzeneted sikeresen elküldtük!', 'success'),", "// showSuccess: () => showStatus('Your message has been sent.', 'success'),"),
("throw new Error(`Szerver hiba: ${response.status}`);", "throw new Error(`Server error: ${response.status}`);"),
("// showError: () => showStatus('Hiba történt.', 'error-state'),", "// showError: () => showStatus('Something went wrong.', 'error-state'),"),
])
s = finish_en(s, "/kapcsolat", "/en/contact", "/en/contact")
write("en/contact.html", s)
print("written en/contact.html")
@@ -0,0 +1,11 @@
# -*- coding: utf-8 -*-
"""/en/download: keeps its text; gets the new English nav + footer; the R-559 header link gives way to the nav switch."""
import sys
sys.path.insert(0, __file__.rsplit("/", 1)[0])
from common import *
from en_common import *
s = head_text("en/download.html")
s = sub1(s, '\n <p><a href="/letoltes" lang="hu" hreflang="hu">Magyar</a></p>', "")
s = finish_en(s, "/letoltes", "/en/download", None)
write("en/download.html", s)
print("written en/download.html")
@@ -0,0 +1,264 @@
# -*- coding: utf-8 -*-
"""/en/faq — the English twin of gyik.html. The JSON-LD is rebuilt from the English visible answers."""
import html, json, re, sys
sys.path.insert(0, __file__.rsplit("/", 1)[0])
from common import *
from en_common import *
s = en_head(read("gyik.html"), "/gyik", "/en/faq")
# the Hungarian JSON-LD is replaced wholesale at the end; translate the visible page only
s = tr(s, [
("<title>GYIK — Gyakran ismételt kérdések otthoni szerver szolgáltatásról | Felhom.eu</title>",
"<title>FAQ — frequently asked questions about the home server service | Felhom.eu</title>"),
('content="Válaszok a leggyakoribb kérdésekre: hardver, telepítés, alkalmazások, biztonság, mentések, költségek. Minden, amit az otthoni szerver szolgáltatásunkról tudni érdemes."',
'content="Answers to the most common questions: hardware, installation, apps, security, backups, costs. Everything worth knowing about our home server service in Hungary."'),
('content="GYIK, gyakran ismételt kérdések, otthoni szerver kérdések, self-hosting FAQ, szerver költségek, szerver biztonság"',
'content="FAQ, frequently asked questions, home server questions, self-hosting FAQ, server costs, server security"'),
('content="GYIK — Otthoni szerver szolgáltatás kérdések és válaszok"', 'content="FAQ — home server service questions and answers"'),
('content="Hardver, telepítés, alkalmazások, biztonság, költségek — minden, amit tudni érdemes."', 'content="Hardware, installation, apps, security, costs — everything worth knowing."'),
('content="GYIK — Otthoni szerver kérdések és válaszok"', 'content="FAQ — home server questions and answers"'),
('content="Minden, amit az otthoni szerver szolgáltatásról tudni érdemes."', 'content="Everything worth knowing about the home server service."'),
("<h1>Gyakran Ismételt <span>Kérdések</span></h1>", "<h1>Frequently asked <span>questions</span></h1>"),
("<p>Minden, amit az otthoni szerver szolgáltatásunkról tudni érdemes — egy helyen, érthetően.</p>", "<p>Everything worth knowing about our home server service — in one place, in plain words.</p>"),
("<h3>Kategóriák</h3>", "<h3>Topics</h3>"),
('<li><a href="#alapkerdesek">Alapkérdések</a></li>', '<li><a href="#basics">The basics</a></li>'),
('<li><a href="#hardver">Hardver & Telepítés</a></li>', '<li><a href="#hardware">Hardware & Installation</a></li>'),
('<li><a href="#alkalmazasok">Alkalmazások & Használat</a></li>', '<li><a href="#apps">Apps & Everyday use</a></li>'),
('<li><a href="#biztonsag">Biztonság & Adatvédelem</a></li>', '<li><a href="#security">Security & Privacy</a></li>'),
('<li><a href="#mentesek">Mentések & Adatbiztonság</a></li>', '<li><a href="#backups">Backups & Data safety</a></li>'),
('<li><a href="#uzemeltet">Üzemeltetés & Támogatás</a></li>', '<li><a href="#support">Running & Support</a></li>'),
('<li><a href="#koltsegek">Költségek</a></li>', '<li><a href="#costs">Costs</a></li>'),
('<li><a href="#technikai">Technikai kérdések</a></li>', '<li><a href="#technical">Technical questions</a></li>'),
('id="alapkerdesek"', 'id="basics"'), ('id="hardver"', 'id="hardware"'), ('id="alkalmazasok"', 'id="apps"'),
('id="biztonsag"', 'id="security"'), ('id="mentesek"', 'id="backups"'), ('id="uzemeltet"', 'id="support"'),
('id="koltsegek"', 'id="costs"'), ('id="technikai"', 'id="technical"'),
("<h2>Alapkérdések</h2>", "<h2>The basics</h2>"),
("Mi az a Felhom.eu, és mit csináltok pontosan?\n", "What is Felhom.eu, and what exactly do you do?\n"),
("<p>A Felhom.eu egy olyan szolgáltatást nyújt, amely segít az otthoni szerver(ek) telepítésében és üzemeltetésében. Kulcsrakész megoldásokat kínálunk: megválasztjuk a hardvert, beállítjuk a szoftvereket, biztosítjuk a mentéseket, és folyamatosan figyelemmel kísérjük a rendszert.</p>",
"<p>Felhom.eu is a service for households in Hungary that helps install and run a home server. We offer turnkey set-ups: we choose the hardware, set up the software, take care of the backups, and keep an eye on the system all the time.</p>"),
("<p>A cél egyszerű: a nagy felhőszolgáltatók (Google, Microsoft, Apple) helyett a saját otthonodban futó szerveren tárolhatod az adataidat — anélkül, hogy neked kellene szakértőnek lenned.</p>",
"<p>The aim is simple: instead of the big cloud providers (Google, Microsoft, Apple), you keep your data on a server in your own home — without having to be an expert yourself.</p>"),
("Kinek való ez a szolgáltatás?\n", "Who is this service for?\n"),
("<p>Mindenkinek, aki szeretné visszavenni az irányítást a digitális élete felett. Tipikus ügyfeleink:</p>", "<p>Anyone who wants to take back control of their digital life. Typical customers:</p>"),
("<p><strong>Családok</strong>, akik szeretnék a fotóikat, videóikat biztonságban tudni, és nem akarnak havonta fizetni a felhőtárhelyért.</p>",
"<p><strong>Families</strong> who want their photos and videos safe, and do not want to pay for cloud storage every month.</p>"),
("<p><strong>Adatvédelem-tudatos felhasználók</strong>, akik nem akarják, hogy a Google vagy a Microsoft hozzáférjen a dokumentumaikhoz.</p>",
"<p><strong>Privacy-minded people</strong> who do not want Google or Microsoft to have access to their documents.</p>"),
("<p><strong>Médiarajongók</strong>, akik saját Netflix/Spotify-szerű rendszert szeretnének a filmjeikhez és zenéjükhöz.</p>",
"<p><strong>Media fans</strong> who want their own Netflix- or Spotify-like system for their films and music.</p>"),
("<p><strong>Kisvállalkozások</strong>, amelyeknek szükségük van dokumentumkezelésre, belső wikire, vagy időpontfoglalásra — saját kontroll alatt.</p>",
"<p><strong>Small businesses</strong> that need document management, an internal wiki or appointment booking — under their own control.</p>"),
("Kell hozzá bármilyen technikai tudás?\n", "Do I need any technical knowledge?\n"),
("<p><strong>Nem.</strong> A teljes telepítést és beállítást mi végezzük. A te dolgod annyi, hogy használod az alkalmazásokat — böngészőn vagy mobilon keresztül, pont úgy, mint egy Google vagy Apple szolgáltatást.</p>",
"<p><strong>No.</strong> We do the whole installation and set-up. Your part is to use the apps — in a browser or on your phone, just like a Google or Apple service.</p>"),
("<p>A telepítés után egy rövid bemutatón végigvezetünk, hogy magabiztosan használd a rendszert.</p>", "<p>After the installation we walk you through it in a short session, so you can use the system with confidence.</p>"),
("Mi a különbség a Felhom.eu és a Synology/QNAP NAS szolgáltatásai között?\n", "How is Felhom.eu different from a Synology or QNAP NAS?\n"),
("<p>A Synology és QNAP elsősorban fájltárolásra készült NAS (hálózati adattároló) eszközök, saját zárt szoftverrel. Mi ezzel szemben egy nyílt, Docker-alapú rendszert kínálunk, amelyre a katalógus alkalmazásai pár kattintással telepíthetők — fotókezelőtől a média szerveren át a jelszókezelőig.</p>",
"<p>Synology and QNAP make NAS (network storage) devices built mainly for storing files, with their own closed software. We offer an open, Docker-based system instead, where the catalog's apps install in a few clicks — from a photo manager to a media server to a password manager.</p>"),
("<p>A NAS-ok korlátozott alkalmazás-választékot kínálnak és a gyártó szoftverfrissítéseitől függenek. Nálunk 56 alkalmazásból válogathatsz, és ha valami hiányzik, megnézzük, felvehető-e.</p>",
"<p>A NAS offers a limited choice of apps and depends on the maker's software updates. With us you choose from 56 apps, and if something is missing, we look at whether it can be added.</p>"),
("Hogyan néz ki a folyamat az elejétől a végéig?\n", "What does the process look like, from start to finish?\n"),
("<p><strong>1. Egyeztetés</strong> — Megbeszéljük, mire van szükséged: milyen alkalmazásokat használnál, mennyi tárhelyet szeretnél, és mi a költségkereted.</p>",
"<p><strong>1. A first talk</strong> — We talk about what you need: which apps you would use, how much storage you want, and what your budget is.</p>"),
("<p><strong>2. Hardver kiválasztás</strong> — Segítünk megtalálni az optimális gépet, ami illeszkedik az igényeidhez és a büdzsédhez.</p>",
"<p><strong>2. Choosing the hardware</strong> — We help you find the right machine for your needs and your budget.</p>"),
("<p><strong>3. Telepítés és beállítás</strong> — Feltelepítjük az operációs rendszert, a konténerkezelőt, az alkalmazásokat, beállítjuk a mentéseket és a monitoringot.</p>",
"<p><strong>3. Installation and set-up</strong> — We install the operating system, the container engine and the apps, and set up the backups and the monitoring.</p>"),
("<p><strong>4. Átadás és betanítás</strong> — Bemutatjuk, hogyan használd a rendszert, és válaszolunk minden kérdésedre.</p>",
"<p><strong>4. Handover and training</strong> — We show you how to use the system and answer all your questions.</p>"),
("<p><strong>5. Folyamatos üzemeltetés</strong> — Figyeljük a mentéseket, karbantartjuk a rendszert, és segítünk, ha bármi gond van.</p>",
"<p><strong>5. Ongoing care</strong> — We watch the backups, maintain the system, and help if anything goes wrong.</p>"),
("<h2>Hardver & Telepítés</h2>", "<h2>Hardware & Installation</h2>"),
("Milyen gépre van szükségem?\n", "What machine do I need?\n"),
("<p>A legtöbb otthoni felhasználónak egy <strong>mini PC</strong> (pl. Intel N100 processzorral, 16-32 GB RAM-mal) tökéletesen megfelel. Ezek csendesek, energiatakarékosak (15-35W fogyasztás), és kompaktak — elfér a polcon a router mellett.</p>",
"<p>For most homes a <strong>mini PC</strong> (for example with an Intel N100 processor and 16-32 GB of RAM) is perfectly fine. They are quiet, energy-saving (15-35 W) and compact — one fits on the shelf next to the router.</p>"),
("<p>Ha nagyobb tárhelyet szeretnél (pl. filmes könyvtárhoz), egy NAS-al vagy külső merevlemezekkel bővíthető. Részletesebb konfigurációkat a <a href=\"/technologiak#hardver\">Technológiák</a> oldalon találsz.</p>",
"<p>If you want more storage (for a film library, say), you can add a NAS or external hard drives. More detailed set-ups are on the <a href=\"/en/technology#hardware\">Technology</a> page.</p>"),
("Használhatom a már meglévő régi gépemet?\n", "Can I use an old machine I already have?\n"),
("<p>Sok esetben igen! Egy korábbi asztali PC kiváló szerver lehet. Az egyeztetés során felmérjük, hogy a meglévő géped alkalmas-e a tervezett feladatokra (elég RAM, tároló kapacitás, megbízhatóság).</p>",
"<p>Often, yes! An older desktop PC can make an excellent server. In our first talk we check whether your machine suits what you plan (enough RAM, storage, reliability).</p>"),
("<p>Fontos szempont a <strong>fogyasztás</strong> és a <strong>zajszint</strong> — egy régi gamer PC működik ugyan, de havi szinten jóval többet fogyaszt, mint egy modern mini PC.</p>",
"<p><strong>Power use</strong> and <strong>noise</strong> matter — an old gaming PC will work, but each month it uses far more power than a modern mini PC.</p>"),
("Hova kell tenni a szervert?\n", "Where should the server go?\n"),
("<p>Bárhova, ahol van <strong>áramcsatlakozó</strong> és <strong>hálózati kábel</strong> (ethernet). A legjobb hely egy szekrény, polc, vagy a router melletti terület. A mini PC-k annyira kompaktak és csendesek, hogy a nappali polcán is elférnek.</p>",
"<p>Anywhere with a <strong>power socket</strong> and a <strong>network cable</strong> (Ethernet). A cupboard, a shelf or the spot next to the router is best. Mini PCs are so compact and quiet that they fit on a living-room shelf.</p>"),
("<p>WiFi-n is működhet, de a megbízhatóság és sebesség érdekében mindig kábeles kapcsolatot javaslunk.</p>", "<p>It can work over Wi-Fi, but for reliability and speed we always recommend a cable.</p>"),
("Mennyi áramot fogyaszt egy ilyen szerver?\n", "How much power does a server like this use?\n"),
("<p>Egy modern mini PC tipikusan <strong>15-35 watt</strong> körül fogyaszt terhelés alatt, ami éves szinten nagyjából <strong>5.000–15.000 Ft</strong> villanyszámlát jelent (a tarifától függően). Ez kevesebb, mint egy izzó, és töredéke annak, amit egy felhőszolgáltatás éves előfizetése költene.</p>",
"<p>A modern mini PC typically uses around <strong>15-35 watts</strong> under load, which comes to roughly <strong>5,000–15,000 Ft</strong> of electricity a year (depending on your tariff). That is less than a light bulb, and a fraction of what a year of a cloud subscription would cost.</p>"),
("Mennyire hangos egy ilyen szerver?\n", "How loud is a server like this?\n"),
("<p>A mini PC-k szinte hangtalan működnek — sok modell ventilátort sem tartalmaz, vagy csak néha kapcsol be rövid időre. Hétköznapi használat mellett nyugodtan tarthatod az íróasztalon vagy a hálószoba polcán.</p>",
"<p>Mini PCs are almost silent — many models have no fan at all, or one that only turns on now and then. In everyday use you can keep one on your desk or on a bedroom shelf.</p>"),
("<p>A tower szerverek és rack szervereknél ez már más — ezeket inkább pincébe, kamrába érdemes helyezni.</p>", "<p>Tower and rack servers are a different story — those are better off in a cellar or a storeroom.</p>"),
("Mi történik, ha elmegy az áram?\n", "What happens if the power goes off?\n"),
("<p>A telepítés során a szerver BIOS-át úgy konfiguráljuk (amennyiben az opció rendelkezésre áll), hogy áramszünet után automatikusan bekapcsoljon. Az operációs rendszer és az összes alkalmazás szintén automatikusan elindul a bekapcsolást követően. Ez a beállítás minden telepítésünk alapértelmezett része.</p>",
"<p>During the installation we set the server's BIOS (where the option exists) to switch on by itself after a power cut. The operating system and all the apps then start on their own too. This setting is a standard part of every installation we do.</p>"),
("<p>Áramszünet után a doboz magától helyreáll — ezt valódi áramszünettel is kipróbáltuk. Ha a megbízhatóság kritikus, szünetmentes tápegységet (UPS) is tudunk javasolni, ami néhány perc szünet áthidalására elegendő.</p>",
"<p>After a power cut the box recovers by itself — we have tried this with a real power cut. If reliability is critical, we can also suggest an uninterruptible power supply (UPS) to bridge a few minutes without power.</p>"),
("Milyen internet kapcsolat szükséges?\n", "What internet connection do I need?\n"),
("<p>Otthoni használathoz (helyi hálózaton belül) bármilyen internet elég, mert az alkalmazások a helyi hálózatodon futnak — nem az interneten keresztül. A fotók, filmek, dokumentumok sebessége a routeredtől és a kábeles kapcsolattól függ.</p>",
"<p>For use at home (on your home network) any internet connection will do, because the apps run on your home network — not over the internet. How fast photos, films and documents load depends on your router and your cabling.</p>"),
("<p>Ha <strong>távolról is el szeretnéd érni</strong> a szerveredet (pl. munkahelyről vagy nyaralásból), akkor a feltöltési sebesség (upload) számít: minimum 10 Mbps ajánlott, de minél több, annál jobb.</p>",
"<p>If you <strong>also want to reach</strong> your server from elsewhere (from work or on holiday, say), the upload speed matters: at least 10 Mbps is recommended, and more is better.</p>"),
("<h2>Alkalmazások & Használat</h2>", "<h2>Apps & Everyday use</h2>"),
("Milyen alkalmazásokat tudok futtatni?\n", "Which apps can I run?\n"),
("<p><strong>56 alkalmazást</strong> kínálunk — a teljes listát az <a href=\"/alkalmazasok\">Alkalmazások</a> oldalon találod. Néhány példa:</p>",
"<p>We offer <strong>56 apps</strong> — the full list is on the <a href=\"/en/apps\">Apps</a> page. A few examples:</p>"),
("<p><strong>Immich</strong> — Google Photos alternatíva fotók és videók automatikus mentésével. <strong>Jellyfin/Plex</strong> — saját Netflix filmekhez és sorozatokhoz. <strong>Nextcloud</strong> — fájlok tárolása és megosztása (Google Drive helyett). <strong>Vaultwarden</strong> — jelszókezelő az egész családnak. <strong>Paperless-ngx</strong> — dokumentumok digitalizálása és kereshetővé tétele.</p>",
"<p><strong>Immich</strong> — a Google Photos alternative that backs up photos and videos automatically. <strong>Jellyfin/Plex</strong> — your own Netflix for films and series. <strong>Nextcloud</strong> — store and share files (instead of Google Drive). <strong>Vaultwarden</strong> — a password manager for the whole family. <strong>Paperless-ngx</strong> — scan documents and make them searchable.</p>"),
("<p>Ha valami nincs a listán, kérdezz — megnézzük, felvehető-e a katalógusba.</p>", "<p>If something is not on the list, ask — we will look at whether it can join the catalog.</p>"),
("Tudok később új alkalmazásokat hozzáadni?\n", "Can I add new apps later?\n"),
("<p><strong>Igen, bármikor!</strong> A vezérlőpultból bármelyik alkalmazás telepíthető, pár kattintással. Ha elakadsz, szólj, és segítünk beállítani.</p>",
"<p><strong>Yes, any time!</strong> Any app installs from the Dashboard in a few clicks. If you get stuck, tell us and we will help you set it up.</p>"),
("Telefonról is elérem az alkalmazásokat?\n", "Can I reach the apps from my phone?\n"),
("<p>Igen! A legtöbb alkalmazás böngészőből is tökéletesen használható telefonon. Sok alkalmazásnak emellett dedikált mobilalkalmazása is van (iOS és Android egyaránt). Például az Immich, Nextcloud, Jellyfin, és Vaultwarden mind rendelkeznek kiváló mobil appokkal.</p>",
"<p>Yes! Most apps work perfectly well in a phone's browser. Many also have their own mobile apps (for both iOS and Android). Immich, Nextcloud, Jellyfin and Vaultwarden, for example, all have excellent mobile apps.</p>"),
("Több családtag is használhatja egyszerre?\n", "Can several family members use it at the same time?\n"),
("<p><strong>Igen, de érdemes tudni a jelenlegi korlátot.</strong> A telepített alkalmazások többsége (Immich, Nextcloud, Vaultwarden) támogatja a többfelhasználós működést: bennük minden családtag saját fiókot kaphat, saját bejelentkezéssel. A Felhom <strong>vezérlőpulthoz</strong> viszont jelenleg <strong>egy közös háztartási jelszó</strong> tartozik — külön családtagi fiókok a vezérlőpulton még nincsenek. A vezérlőpult az, ahonnan alkalmazást telepítesz és a mentéseket kezeled; a mindennapi használathoz (fotók, fájlok) az alkalmazások saját fiókjai kellenek.</p>",
"<p><strong>Yes, but know the current limit.</strong> Most of the apps (Immich, Nextcloud, Vaultwarden) support several users: in them every family member can have their own account and their own sign-in. The Felhom <strong>Dashboard</strong>, though, has <strong>one shared household password</strong> today — there are no separate family accounts on the Dashboard yet. The Dashboard is where you install apps and manage backups; for everyday use (photos, files) you use the apps' own accounts.</p>"),
("Át tudom hozni a meglévő adataimat (Google Fotók, iCloud, Dropbox)?\n", "Can I bring over my existing data (Google Photos, iCloud, Dropbox)?\n"),
("<p>Igen! A legtöbb szolgáltatásból exportálhatók az adatok. A Google Takeout segítségével az összes fotódat, dokumentumodat, naptáradat letöltheted, és az Immich, Nextcloud, stb. importálni tudja ezeket. Segítünk a migrációban.</p>",
"<p>Yes! Most services let you export your data. With Google Takeout you can download all your photos, documents and calendar, and Immich, Nextcloud and others can import them. We help with the move.</p>"),
("<p>A folyamat időigénye az adatmennyiségtől függ — néhány száz GB-nyi fotó átvitele akár egy-két napot is igénybe vehet, de a rendszer utána pontosan úgy működik, ahogy a felhőszolgáltatásnál megszoktad.</p>",
"<p>How long it takes depends on how much data there is — moving a few hundred GB of photos can take a day or two, but afterwards the system works just the way you are used to from the cloud service.</p>"),
("Elérem a szerveremet, ha nem vagyok otthon?\n", "Can I reach my server when I am not at home?\n"),
("<p>Igen! A doboz egy titkosított alagúton át, HTTPS-sel érhető el, külön alkalmazás nélkül. Ez azt jelenti, hogy bárhonnan — munkahelyről, nyaralásból, vagy akár külföldről — elérheted a fotóidat, fájljaidat, és minden alkalmazásodat, mintha otthon lennél.</p>",
"<p>Yes! The box is reached through an encrypted tunnel, over HTTPS, with no extra app. That means you can reach your photos, files and all your apps from anywhere — from work, on holiday or even abroad — as if you were at home.</p>"),
("<h2>Biztonság & Adatvédelem</h2>", "<h2>Security & Privacy</h2>"),
("Mennyire biztonságos egy otthoni szerver?\n", "How safe is a home server?\n"),
("<p>Az általunk telepített rendszerek több rétegű védelmet kapnak: automatikus HTTPS titkosítás, rendszeres biztonsági frissítések, saját bejelentkezés minden alkalmazásban, és egyes alkalmazások előtt családi kapu. A szervered a te otthonodon belül van — otthon nem nyitunk portot, kívülről csak egy titkosított alagúton keresztül érhető el.</p>",
"<p>The systems we install get several layers of protection: automatic HTTPS encryption, regular security updates, a sign-in for every app, and a family gate in front of some apps. Your server is inside your home — we open no port there, and from outside it is reached only through an encrypted tunnel.</p>"),
("<p>A legtöbb otthoni szerver ellen nem célzott támadások szoktak irányulni, hanem automatizált szkriptek próbálkoznak. Ezek ellen a megfelelő konfiguráció kiváló védelmet nyújt.</p>",
"<p>Most home servers do not face targeted attacks; what they meet is automated scripts trying their luck. The right configuration protects very well against those.</p>"),
("Ti hozzáfértek az adataimhoz?\n", "Can you get at my data?\n"),
("<p>Ez egy fontos és jogos kérdés, amire őszintén válaszolunk.</p>", "<p>That is an important, fair question, and we answer it honestly.</p>"),
("<p>A támogatás és karbantartás érdekében alapesetben rendelkezünk távoli hozzáféréssel a szerveredhez — ahogy bármely IT szolgáltató a kezelt rendszerekhez. Ez szükséges a mentések ellenőrzéséhez, frissítések telepítéséhez, és hibaelhárításhoz.</p>",
"<p>For support and maintenance we normally have remote access to your server — as any IT provider has to the systems it manages. We need it to check backups, install updates and fix faults.</p>"),
("<p>A távoli mentésed kulcsát viszont <strong>csak te ismered</strong>: a helyreállítási kód egyszer jelenik meg, a te képernyődön, és nálunk nem marad belőle másolat. A távoli mentést így mi sem tudjuk elolvasni.</p>",
"<p>The key to your remote backup, though, <strong>only you know</strong>: the recovery code appears once, on your screen, and we keep no copy of it. So we cannot read the remote backup either.</p>"),
("<p>Fontos: szemben a nagy felhőszolgáltatókkal, nálunk <strong>nincs algoritmikus adatelemzés</strong>, nincs reklámcélú profilozás, és az adataid csak titkosítva hagyják el az otthonodat (a távoli mentés).</p>",
"<p>Important: unlike the big cloud providers, we do <strong>no algorithmic analysis of your data</strong> and no profiling for advertising, and your data leaves your home only encrypted (the remote backup).</p>"),
("Mi van, ha betörnek a hálózatomba?\n", "What if someone breaks into my network?\n"),
("<p>A szerver alkalmazásai izolált konténerekben futnak, ami korlátozza a kár mértékét, ha egy alkalmazás sérülékeny lenne. A rendszeres frissítések, és hogy otthon nincs nyitott port, csökkentik a kockázatot.</p>",
"<p>The server's apps run in isolated containers, which limits the damage if one app has a weakness. Regular updates, and having no open port at home, keep the risk down.</p>"),
("<p>A mentéseidből egy titkosított másolat külön helyen (offsite) is tárolódik — így egy zsarolóvírus-támadás után is van egy távol tárolt másolat.</p>",
"<p>An encrypted copy of your backups is also kept in a separate place (off-site) — so even after a ransomware attack, there is a copy stored far away.</p>"),
("GDPR szempontból ez rendben van?\n", "Is this all right under the GDPR?\n"),
("<p>Személyes, otthoni használatra a GDPR nem vonatkozik közvetlenül (a „háztartási kivétel\" alapján). Ha viszont kisvállalkozásként használod, az adataid a saját infrastruktúrádon vannak — nem harmadik félnél —, ami GDPR szempontból a lehető legjobb kiindulás. Természetesen nem vagyunk jogi tanácsadók, konkrét jogi kérdésekben szakembert érdemes keresni.</p>",
"<p>For personal use at home the GDPR does not apply directly (under the \"household exemption\"). If you use it as a small business, your data sits on your own infrastructure — not with a third party — which is the best possible start under the GDPR. We are not lawyers, of course: for a specific legal question, ask a professional.</p>"),
("<h2>Mentések & Adatbiztonság</h2>", "<h2>Backups & Data safety</h2>"),
("Hogyan oldják meg a mentéseket?\n", "How are backups done?\n"),
("<p>A <strong>3-2-1 mentési szabályt</strong> követjük: 3 másolat az adatokról, 2 különböző tárolón, 1 távoli helyen. Ez iparági szabvány, amit a legnagyobb cégek is alkalmaznak. A dobozon ez három szint: egy mentés a dobozon, egy másodpéldány egy második meghajtón, és egy titkosított másolat egy távoli tárhelyen.</p>",
"<p>We follow the <strong>3-2-1 backup rule</strong>: 3 copies of the data, on 2 different kinds of storage, 1 of them far away. It is an industry standard the largest companies use too. On the box this means three levels: a backup on the box, a second copy on a second drive, and an encrypted copy on remote storage.</p>"),
("<p>A mentések <strong>automatikusan</strong>, éjszaka futnak. Az adatbázisok és fájlok inkrementálisan mentődnek — tehát csak a változások kerülnek mentésre, ami helyet és időt takarít meg. Részletes leírást a <a href=\"/biztonsagimentes\">Mentések</a> oldalon találsz.</p>",
"<p>The backups run <strong>automatically</strong>, at night. Databases and files are backed up incrementally — only the changes are saved, which saves space and time. The details are on the <a href=\"/en/backups\">Backups</a> page.</p>"),
("Mi történik, ha meghibásodik a merevlemez?\n", "What happens if a hard drive fails?\n"),
("<p>Ezért vannak a mentések! A második meghajtón és a távoli tárhelyen lévő másolatokból az alkalmazásaid adatai visszahozhatók. A doboz figyeli a lemezek állapotát és a szabad helyet, és előre figyelmeztet, ha probléma van.</p>",
"<p>That is what backups are for! Your apps' data can be brought back from the copies on the second drive and on remote storage. The box watches the drives' health and the free space, and warns ahead of time if there is a problem.</p>"),
("Honnan tudom, hogy a mentések valóban működnek?\n", "How do I know the backups really work?\n"),
("<p>Központi monitoring rendszert üzemeltetünk, amely <strong>minden nap</strong> ellenőrzi, hogy a mentések sikeresen lefutottak-e. Ha bármi probléma van — sikertelen mentés, tárhelyhiány, vagy a szerver nem elérhető — azonnal értesítést kapunk, és szükség esetén te is.</p>",
"<p>We run a central monitoring system that checks <strong>every day</strong> whether the backups ran successfully. If there is any problem — a failed backup, low storage, or the server not answering — we are notified at once, and so are you when it matters.</p>"),
("<p>Ez nem egy „beállítom és remélem működik\" megoldás — aktívan figyeljük, és időszakosan visszaállítási teszteket is végzünk.</p>",
"<p>This is not a \"set it up and hope it works\" arrangement — we watch it actively, and we also run restore tests from time to time.</p>"),
("Mi az az „offsite backup\"?\n", "What is an \"off-site backup\"?\n"),
("<p>Az offsite (távoli) backup azt jelenti, hogy az adataidról egy másolat fizikailag más helyen is megvan. A Felhom dobozon ez egy általunk biztosított tárhely, ahova a másolat titkosítva kerül. Ha otthon bármi katasztrófa történne (tűz, lopás, vízkár), a távoli másolat akkor is megmarad.</p>",
"<p>An off-site (remote) backup means a copy of your data also exists in a physically different place. On a Felhom box that is storage we provide, where the copy goes encrypted. If disaster struck at home (fire, theft, flood), the remote copy would still be there.</p>"),
("<p>Az offsite mentés mindig <strong>titkosítva</strong> történik — a távoli helyen senki nem tudja elolvasni az adataidat, még ha hozzáfér is a fájlokhoz.</p>",
"<p>The off-site backup is always <strong>encrypted</strong> — nobody at the remote place can read your data, even if they can reach the files.</p>"),
("<h2>Üzemeltetés & Támogatás</h2>", "<h2>Running & Support</h2>"),
("Mi történik a telepítés után? Magamra maradok?\n", "What happens after the installation? Am I left on my own?\n"),
("<p><strong>Nem!</strong> A Felhom.eu nem egy „telepítem és távozom\" szolgáltatást nyújt. Folyamatos üzemeltetést biztosítunk: monitorozás, szoftverfrissítések, mentés-ellenőrzés, és támogatás, ha bármi kérdésed vagy problémád van.</p>",
"<p><strong>No!</strong> Felhom.eu is not an \"install and leave\" service. We keep running it for you: monitoring, software updates, backup checks, and support whenever you have a question or a problem.</p>"),
("Hogyan történik a szoftverfrissítés?\n", "How are software updates done?\n"),
("<p>Az alkalmazások konténerekben futnak, ami megkönnyíti a frissítést. Mi figyelemmel kísérjük az új verziókat, és egy új verzió csak akkor jut el hozzád, ha előbb kipróbáltuk. A doboz rendszere a biztonsági frissítéseket magától megkapja.</p>",
"<p>The apps run in containers, which makes updating easier. We follow new versions, and a new version reaches you only after we have tried it. The box's system gets its security updates by itself.</p>"),
("<p>Minden alkalmazásfrissítés előtt friss mentés készül, és ha az új verzió nem indul el rendesen, a doboz magától visszaállítja az előzőt, az adataiddal együtt.</p>",
"<p>Before every app update a fresh backup is made, and if the new version does not start properly, the box puts the previous one back by itself, with your data.</p>"),
("Hogyan kérjek segítséget, ha valami nem működik?\n", "How do I ask for help if something does not work?\n"),
("<p>Egyszerűen írj emailt a <strong>support@felhom.eu</strong> címre. A legtöbb problémát távolról, a te közreműködésed nélkül meg tudjuk oldani. Komolyabb hardveres kérdéseknél személyesen is segítünk.</p>",
"<p>Just e-mail <strong>support@felhom.eu</strong>. We can solve most problems remotely, without you having to do anything. For bigger hardware issues we also help in person.</p>"),
("Mi van, ha fel akarom mondani a szolgáltatást?\n", "What if I want to cancel the service?\n"),
("<p>A szerver és az összes adat a tiéd — mindig is az volt. Ha úgy döntesz, hogy nem kéred tovább a szolgáltatást, a doboz az otthoni hálózaton tovább működik, de nem kapod a frissítéseket, a felügyeletet és a támogatást. A távoli elérés és a távoli mentés a szolgáltatás része: hogy mi lesz velük, azt a felmondáskor egyeztetjük.</p>",
"<p>The server and all the data are yours — they always were. If you decide you no longer want the service, the box keeps working on your home network, but you no longer get updates, monitoring or support. Remote access and the remote backup are part of the service: what happens to them, we agree when you cancel.</p>"),
("<p>Nincs hűségidő, nincs felmondási díj. Ha van technikai tudásod (vagy időközben szereztél), akár magad is üzemeltetheted tovább a rendszert. Az adatok és az alkalmazások nálad maradnak.</p>",
"<p>No minimum term, no cancellation fee. If you have the technical know-how (or have picked it up along the way), you can even keep running the system yourself. The data and the apps stay with you.</p>"),
("Mi történik, ha a Felhom.eu megszűnik?\n", "What happens if Felhom.eu shuts down?\n"),
("<p>A szervered és az adataid a tiéd. Az alkalmazások szinte mind nyílt forráskódúak, ami azt jelenti, hogy bárki más is üzemeltetheti őket — nem függsz egyetlen szolgáltatótól sem. Ez éppen az egyik legnagyobb előnye a nyílt forráskódú megoldásoknak: <strong>nincs vendor lock-in</strong>.</p>",
"<p>Your server and your data are yours. Almost all the apps are open source, which means anyone else can run them — you do not depend on a single provider. That is one of the biggest advantages of open-source software: <strong>no vendor lock-in</strong>.</p>"),
("<p>Ezzel szemben, ha a Google vagy az Apple megszünteti egy szolgáltatását, az adataid velük együtt tűnnek el.</p>", "<p>By contrast, when Google or Apple shuts down a service, your data disappears along with it.</p>"),
("Mi van, ha elköltözöm?\n", "What if I move house?\n"),
("<p>A szervered egy kompakt doboz — lecsatlakoztatod, átviszed az új lakásba, becsatlakoztatod, és minden megy tovább. Ha változik az internet szolgáltatód, lehet, hogy a távoli elérés konfigurációját frissíteni kell — ebben segítünk.</p>",
"<p>Your server is a compact box — unplug it, take it to your new home, plug it in, and everything carries on. If your internet provider changes, the remote access set-up may need updating — we help with that.</p>"),
("<h2>Költségek</h2>", "<h2>Costs</h2>"),
("Mennyibe kerül az egész?\n", "How much does it all cost?\n"),
("<p>A költség két részből áll:</p>", "<p>The cost has two parts:</p>"),
("<p><strong>Egyszeri költségek:</strong> A hardver ára (80.000–400.000+ Ft, konfigurációtól függően) és a telepítési díj. A hardvert te vásárolod meg — a tiéd, örökre.</p>",
"<p><strong>One-off costs:</strong> the hardware (80,000–400,000+ Ft, depending on the set-up) and the installation fee. You buy the hardware — it is yours, for good.</p>"),
("<p><strong>Havi üzemeltetési díj:</strong> A monitoring, támogatás, frissítések, és mentés-felügyelet díja. Ez a kiválasztott csomagtól függ.</p>",
"<p><strong>A monthly management fee:</strong> for monitoring, support, updates and backup supervision. It depends on the package you choose.</p>"),
("<p>Pontos árajánlatot az egyeztetés után adunk — mert az igények nagyon eltérőek lehetnek. Írj nekünk, és személyre szabott ajánlatot készítünk!</p>",
"<p>We give an exact quote after our first talk — needs vary a lot. Write to us and we will make you a personal offer!</p>"),
("Megéri anyagilag a felhőszolgáltatásokhoz képest?\n", "Is it worth it compared with cloud services?\n"),
("<p>Hosszú távon <strong>szinte mindig</strong>. Egy tipikus példa: Google One 2TB évi ~36.000 Ft, iCloud 2TB évi ~45.000 Ft, Spotify család évi ~30.000 Ft, jelszókezelő évi ~15.000 Ft. Ezek együtt évi 120.000+ Ft — és ez csak néhány szolgáltatás.</p>",
"<p>In the long run, <strong>almost always</strong>. A typical example: Google One 2 TB is about 36,000 Ft a year, iCloud 2 TB about 45,000 Ft, Spotify Family about 30,000 Ft, a password manager about 15,000 Ft. Together that is 120,000+ Ft a year — and that is only a few services.</p>"),
("<p>Egy mini PC-s szerver 2-3 év alatt „behozza\" az árát, és utána gyakorlatilag csak áramot fizetsz. Emellett korlátlan tárhelyed van — nem 2 TB, hanem amennyit a lemezeid bírnak.</p>",
"<p>A mini PC server \"pays for itself\" in 2-3 years, and after that you mostly pay for electricity. And your storage has no fixed cap — not 2 TB, but as much as your disks hold.</p>"),
("Vannak rejtett költségek?\n", "Are there hidden costs?\n"),
("<p>Nincsenek. A felmerülő költségek: hardver (egyszeri), telepítés (egyszeri), havi üzemeltetés (opcionális), és villanyáram (havi néhány ezer forint). Az alkalmazások ingyenesen használhatók, és szinte mind <strong>nyílt forráskódú</strong> — nincs licencdíj az alkalmazásokért. (Néhánynak van fizetős extrája, például a Plex Pass; ez választható.)</p>",
"<p>No. The costs are: hardware (one-off), installation (one-off), monthly management (optional), and electricity (a few thousand forints a month). The apps are free to use, and almost all of them are <strong>open source</strong> — there is no licence fee for the apps. (A few have paid extras, such as Plex Pass; those are optional.)</p>"),
("<p>A távoli mentés tárhelyének van havi költsége, ami a mentett adatmennyiségtől függ — ezt az ajánlatban előre leírjuk.</p>",
"<p>The remote backup's storage has a monthly cost that depends on how much data is backed up — we state it in the offer up front.</p>"),
("<h2>Technikai kérdések</h2>", "<h2>Technical questions</h2>"),
("Milyen operációs rendszer fut a szerveren?\n", "What operating system runs on the server?\n"),
("<p>A szerver alapja a <strong>Proxmox VE</strong> — egy professzionális, Debian-alapú virtualizációs rendszer, amelyre a teljes Felhom rendszert építjük. Az alkalmazásaid Docker konténerekben futnak egy elkülönített rendszerkörnyezetben, a Proxmox alapra telepítve. Így a teljes rendszerkörnyezet is menthető, és ebből visszaállítható.</p>",
"<p>The server's base is <strong>Proxmox VE</strong> — a professional, Debian-based virtualisation system the whole Felhom system is built on. Your apps run in Docker containers in a separate system environment installed on the Proxmox base. So the whole system environment can be backed up too, and restored from that backup.</p>"),
("Mi az a Proxmox, és miért használjátok?\n", "What is Proxmox, and why do you use it?\n"),
("<p>A Proxmox egy professzionális virtualizációs alap — ugyanaz a fajta technológia, amelyet adatközpontok és cégek használnak. Minden Felhom dobozon ott van, csendben a háttérben; neked nem kell vele foglalkoznod. Ez teszi lehetővé a legfontosabb biztonsági funkciókat:</p>",
"<p>Proxmox is a professional virtualisation base — the same kind of technology data centres and companies use. It is on every Felhom box, quietly in the background; you do not need to deal with it. It makes the most important safety features possible:</p>"),
("<strong>Mentés frissítés előtt</strong> — egy alkalmazás frissítése előtt friss mentés készül, és ha az új verzió nem indul el, a doboz magától visszaállítja az előzőt.</p>",
"<strong>A backup before each update</strong> — before an app is updated, a fresh backup is made, and if the new version does not start, the box puts the previous one back by itself.</p>"),
("<strong>Az egész rendszer menthető és visszaállítható</strong> — nem csak az adataid, hanem a teljes dobozod is mentésbe kerül.</p>",
"<strong>The whole system can be backed up and restored</strong> — not only your data: the whole box goes into a backup too.</p>"),
("<strong>Tiszta elkülönítés</strong> — a kezelőréteg és az alkalmazásaid elkülönítve futnak, így stabilabb és biztonságosabb a rendszer.</p>",
"<strong>Clean separation</strong> — the control layer and your apps run apart, which makes the system more stable and safer.</p>"),
("Mi az a Docker / konténer?\n", "What is Docker / a container?\n"),
("<p>A konténer egy „doboz\", amiben egy alkalmazás és minden függősége együtt él. Gondolj rá úgy, mintha minden alkalmazás a saját kis lakásában élne — nem zavarják egymást, egyszerű frissíteni vagy eltávolítani őket, és ha az egyik elromlik, a többi nem érinti.</p>",
"<p>A container is a \"box\" in which an app lives together with everything it depends on. Think of it as every app living in its own little flat — they do not disturb each other, they are easy to update or remove, and if one breaks, the others are not affected.</p>"),
("<p>Erről bővebben a <a href=\"/technologiak#kontenerek\">Technológiák</a> oldalon olvashatsz.</p>", "<p>There is more about this on the <a href=\"/en/technology#containers\">Technology</a> page.</p>"),
("Saját domain nevet is használhatok?\n", "Can I use my own domain name?\n"),
("<p>Igen! Ha van saját domain neved (pl. <em>enszerverem.hu</em>), beállíthatjuk, hogy az alkalmazásaid azon keresztül legyenek elérhetők (pl. <em>fotok.enszerverem.hu</em>, <em>filmek.enszerverem.hu</em>). Ha nincs domain neved, segítünk az igényléssel — az éves költsége néhány ezer forint.</p>",
"<p>Yes! If you have your own domain name (for example <em>myserver.hu</em>), we can make your apps reachable through it (for example <em>photos.myserver.hu</em>, <em>films.myserver.hu</em>). If you do not have one, we help you get one — it costs a few thousand forints a year.</p>"),
("Később bővíthetem a tárhelyet?\n", "Can I add storage later?\n"),
("<p><strong>Igen!</strong> Egy új merevlemez vagy SSD hozzáadása a legtöbb esetben egyszerű bővítés. Akár USB-s külső meghajtóval is bővíthetsz, ha a gépedben elfogytak a helyek. A rendszer úgy van felépítve, hogy rugalmasan alkalmazkodjon a növekvő igényekhez.</p>",
"<p><strong>Yes!</strong> Adding a new hard drive or SSD is usually a simple upgrade. You can even add an external USB drive if your machine has no free bays left. The system is built to grow with your needs.</p>"),
("Használhatok VPN-t a szerver eléréséhez?\n", "Can I use a VPN to reach the server?\n"),
("<p>Nem kell hozzá: a doboz alapból egy titkosított alagúton át, HTTPS-sel érhető el bárhonnan, külön alkalmazás nélkül.</p>",
"<p>You do not need one: by default the box is reached from anywhere through an encrypted tunnel, over HTTPS, with no extra app.</p>"),
("<p>Háztartási VPN-elérést (amikor a telefonod vagy a laptopod úgy csatlakozik, mintha otthon lenne) ma nem állítunk be a dobozon. Ha erre van szükséged, kérdezz rá.</p>",
"<p>We do not set up household VPN access on the box today (where your phone or laptop connects as if it were at home). If you need that, ask us.</p>"),
("<h2>Nem találtad a <span>válaszodat?</span></h2>", "<h2>Didn't find <span>your answer?</span></h2>"),
("<p>Írj nekünk bátran — szívesen válaszolunk minden kérdésedre.</p>", "<p>Just write to us — we are happy to answer any question.</p>"),
('<a href="/#kapcsolat" class="cta-button">Kapcsolatfelvétel →</a>', '<a href="/en/contact" class="cta-button">Get in touch →</a>'),
("// Open FAQ from URL hash (e.g., gyik.html#mentesek)", "// Open FAQ from URL hash (e.g., /en/faq#backups)"),
])
def rebuild_faq_jsonld(page):
items = re.findall(r'<button class="faq-question">\s*(.*?)\s*<svg class="faq-chevron".*?<div class="faq-answer-inner">(.*?)</div></div>', page, re.S)
ents = []
for q, a in items:
parts = re.findall(r"<(?:p|li)[^>]*>(.*?)</(?:p|li)>", a, re.S)
text = " ".join(re.sub(r"\s+", " ", html.unescape(re.sub(r"<[^>]+>", "", p))).strip() for p in parts)
ents.append({"@type": "Question", "name": html.unescape(q.strip()), "acceptedAnswer": {"@type": "Answer", "text": text}})
body = json.dumps({"@context": "https://schema.org", "@type": "FAQPage", "mainEntity": ents}, ensure_ascii=False, indent=2)
body = "\n".join(" " + l for l in body.split("\n"))
new, n = re.subn(r'<script type="application/ld\+json">.*?</script>',
lambda m: '<script type="application/ld+json">\n' + body + "\n </script>", page, count=1, flags=re.S)
assert n == 1
return new, len(ents)
s, n = rebuild_faq_jsonld(s)
s = finish_en(s, "/gyik", "/en/faq", "/en/faq")
write("en/faq.html", s)
print("written en/faq.html,", n, "questions")
@@ -0,0 +1,195 @@
# -*- coding: utf-8 -*-
"""/en/ — the English twin of index.html, made by replacing every Hungarian string of the FINAL Hungarian page,
so the two pages keep the same structure. Every replacement must match (sub1 counts)."""
import re, sys
sys.path.insert(0, __file__.rsplit("/", 1)[0])
from common import *
from en_common import *
s = read("index.html")
HEAD = [
('<html lang="hu">', '<html lang="en">'),
('content="Otthoni szerver a saját otthonodban: fényképek, dokumentumok, médiatár. 56 telepíthető alkalmazás, háromszintű automatikus mentés, magyar vagy angol nyelvű vezérlőpult. Most zárt teszt indul."',
'content="A home server in your own home in Hungary: photos, documents, media. 56 apps to install, automatic backups on three levels, a dashboard in English or Hungarian. A closed test is starting now."'),
("<title>Felhom.eu — Saját felhőd, saját szabályaid | Otthoni szerver szolgáltatás</title>",
"<title>Felhom.eu — Your own cloud, your own rules | A home server service in Hungary</title>"),
('content="otthoni szerver, saját felhő, self-hosting, Google Drive alternatíva, iCloud alternatíva, adatvédelem, nyílt forráskódú, szerver telepítés, szerver üzemeltetés, Nextcloud, Immich, Magyarország"',
'content="home server, own cloud, self-hosting, Google Drive alternative, iCloud alternative, privacy, open source, server installation, server management, Nextcloud, Immich, Hungary"'),
('<link rel="canonical" href="https://felhom.eu/">', '<link rel="canonical" href="https://felhom.eu/en/">'),
('<meta property="og:url" content="https://felhom.eu/">', '<meta property="og:url" content="https://felhom.eu/en/">'),
('<meta property="og:title" content="Felhom.eu — Saját felhőd, saját szabályaid">', '<meta property="og:title" content="Felhom.eu — Your own cloud, your own rules">'),
('<meta property="og:description" content="Otthoni szerver a te otthonodban: fényképek, dokumentumok, médiatár — automatikus mentéssel, és egy helyreállítási kóddal, amit csak te ismersz. Most zárt teszt indul.">',
'<meta property="og:description" content="A home server in your own home in Hungary: photos, documents, media — with automatic backups and a recovery code only you know. A closed test is starting now.">'),
('<meta property="og:image" content="https://felhom.eu/assets/og-image.png">', '<meta property="og:image" content="https://felhom.eu/assets/og-image-en.png">'),
('<meta property="og:image:alt" content="Felhom.eu — Saját felhőd, saját szabályaid. Zárt teszt indul.">', '<meta property="og:image:alt" content="Felhom.eu — Your own cloud, your own rules. Closed test starting.">'),
('<meta property="og:locale" content="hu_HU">', '<meta property="og:locale" content="en_GB">'),
('<meta name="twitter:title" content="Felhom.eu — Saját felhőd, saját szabályaid">', '<meta name="twitter:title" content="Felhom.eu — Your own cloud, your own rules">'),
('<meta name="twitter:description" content="Otthoni szerver a te otthonodban: fényképek, dokumentumok, médiatár — automatikus mentéssel, és egy helyreállítási kóddal, amit csak te ismersz.">',
'<meta name="twitter:description" content="A home server in your own home in Hungary: photos, documents, media — with automatic backups and a recovery code only you know.">'),
('<meta name="twitter:image" content="https://felhom.eu/assets/og-image.png">', '<meta name="twitter:image" content="https://felhom.eu/assets/og-image-en.png">'),
('"description": "Professzionális otthoni szerver telepítés és üzemeltetés Magyarországon.",', '"description": "Professional home server installation and management in Hungary.",'),
('"name": "Magyarország"', '"name": "Hungary"'),
('"serviceType": ["Szerver telepítés", "Szerver üzemeltetés", "Self-hosting"],', '"serviceType": ["Server installation", "Server management", "Self-hosting"],'),
]
BODY = [
("<h1>Saját felhőd,<br><span>saját szabályaid</span></h1>", "<h1>Your own cloud,<br><span>your own rules</span></h1>"),
("<p>Professzionális otthoni szerver telepítés és üzemeltetés. Te irányítasz, mi segítünk.</p>",
"<p>Professional home server installation and management, for households in Hungary. You are in charge; we help.</p>"),
('<a href="#zart-teszt" class="cta-button">Jelentkezem tesztelőnek →</a>', '<a href="#closed-test" class="cta-button">I want to be a tester →</a>'),
('alt="Felhom.eu - Otthoni szerver szolgáltatás"', 'alt="Felhom.eu - home server service"'),
('id="mit-tud"', 'id="what-it-does"'),
("<h2>Mit tud <span>a doboz ma?</span></h2>", "<h2>What the box <span>does today</span></h2>"),
("<p>Nem terv, nem ígéret — ezek ma működő funkciók egy éles Felhom dobozon</p>", "<p>Not a plan, not a promise — these work today on a live Felhom box</p>"),
("Fényképek, dokumentumok, média</h3>", "Photos, documents, media</h3>"),
("<p>A telefonod fotói automatikusan feltöltődnek a saját gépedre. A beszkennelt papírjaidat a rendszer felismeri és kereshetővé teszi. A filmjeid, zenéid böngészőből és mobil appból is mennek.</p>",
"<p>Your phone's photos upload to your own machine on their own. Your scanned papers are read and made searchable. Your films and music play in the browser and in mobile apps.</p>"),
("Hálózati megosztás</h3>", "Network shares</h3>"),
("<p>A doboz mappái megjelennek a Windows Intézőben és a Mac Finderben — úgy húzod rájuk a fájlokat, mint bármelyik másik mappára. Megosztásonként beállítható, hogy csak olvasható legyen.</p>",
"<p>The box's folders appear in Windows File Explorer and the Mac Finder — you drag files onto them like onto any other folder. Each share can be set to read-only.</p>"),
("Háromszintű automatikus mentés</h3>", "Automatic backups on three levels</h3>"),
("<p>Minden éjjel készül egy mentés a dobozon, egy másodpéldány egy második meghajtóra (ha van), és egy titkosított másolat egy távoli tárhelyre. A megosztott mappáid a második meghajtón és a távoli másolatban is benne vannak.</p>",
"<p>Every night the box makes a backup on the box, a second copy on a second drive (if there is one), and an encrypted copy on remote storage. Your shared folders are in the second-drive copy and in the remote copy too.</p>"),
("Helyreállítási kód, amit csak te ismersz</h3>", "A recovery code only you know</h3>"),
("<p>A távoli mentésed titkosításának kulcsa egy helyreállítási kód, ami pontosan egyszer jelenik meg — a te képernyődön. Mi nem ismerjük, és nem tudjuk visszaállítani. Ez azt is jelenti, hogy a kód nélkül a távoli mentés nekünk sem olvasható: tedd el jól.</p>",
"<p>The key to your remote backup's encryption is a recovery code that appears exactly once — on your screen. We do not know it and cannot recreate it. That also means that without the code, we cannot read the remote backup either: keep it somewhere safe.</p>"),
("56 telepíthető alkalmazás</h3>", "56 apps to install</h3>"),
("<p>A vezérlőpultból telepíthetők, pár kattintással — parancssor nélkül. Szinte mind nyílt forráskódú (a kivételeket az alkalmazások oldalán jelöljük), és mind a te gépeden fut.</p>",
"<p>You install them from the Dashboard in a few clicks — no command line. Almost all of them are open source (the exceptions are marked on the apps page), and every one runs on your machine.</p>"),
("Távoli felügyelet, automatikus frissítés</h3>", "Remote monitoring, automatic updates</h3>"),
("<p>Figyeljük a doboz állapotát. A vezérlőpult magától frissül a legutóbbi kiadásra, és a doboz rendszere is megkapja a biztonsági frissítéseket — neked nem kell csinálnod semmit. Ha valami elromlik, mi látjuk.</p>",
"<p>We watch how the box is doing. The Dashboard updates itself to the latest release, and the box's system gets its security updates too — you do not have to do anything. If something breaks, we see it.</p>"),
("Óvatos alkalmazásfrissítés</h3>", "Careful app updates</h3>"),
("<p>Egy alkalmazás új verziója csak akkor jut el hozzád, ha előbb kipróbáltuk. Frissítés előtt friss mentés készül, és ha az új verzió nem indul el rendesen, a doboz magától visszaállítja az előzőt, az adataiddal együtt.</p>",
"<p>A new version of an app reaches you only after we have tried it. Before the update the box makes a fresh backup, and if the new version does not start properly, the box puts the previous one back by itself, with your data.</p>"),
("Magyarul vagy angolul</h3>", "In English or Hungarian</h3>"),
("<p>A vezérlőpult magyarul és angolul is használható. A nyelvet a háztartás választja, és a dobozról érkező e-mailek is ezt a nyelvet követik.</p>",
"<p>The Dashboard works in English and in Hungarian. The household picks the language, and the e-mails about the box follow it.</p>"),
("Internetkimaradáskor is működik</h3>", "Works when the internet is down</h3>"),
("<p>A doboz az otthonodban van. Ha elmegy a net, az otthoni hálózaton továbbra is eléred a fotóidat és a fájljaidat — nem egy távoli szolgáltató engedélyétől függenek.</p>",
"<p>The box is in your home. If the internet goes down, you still reach your photos and files on your home network — they do not depend on a faraway provider's permission.</p>"),
("Visszaállítás, ha baj van</h3>", "Restore when something goes wrong</h3>"),
("<p>Egy törölt fájl visszahozható a mentésből. Ha maga a doboz megy tönkre, az újratelepített rendszerre a titkosított távoli mentésből visszahozhatók az alkalmazásaid adatai — a helyreállítási kóddal.</p>",
"<p>A deleted file can be brought back from the backup. If the box itself breaks, your apps' data can be brought back from the encrypted remote backup onto the reinstalled system — with the recovery code.</p>"),
("<h2>Miért ne a <span>felhőszolgáltatók?</span></h2>", "<h2>Why not the <span>cloud providers?</span></h2>"),
("<p>A nagy techcégek kényelmes megoldásokat kínálnak, de van néhány hátrány...</p>", "<p>The big tech companies offer convenient services, but there are some downsides...</p>"),
("<h3>Folyamatos költségek</h3>", "<h3>Costs that never stop</h3>"),
("<p>Havonta fizetsz a tárhelyért, a prémium funkciókért, és az árak csak nőnek idővel.</p>", "<p>You pay every month for storage and premium features, and the prices only go up.</p>"),
("<h3>Adataid másé</h3>", "<h3>Your data, in someone else's hands</h3>"),
("<p>A fotóid, dokumentumaid, személyes adataid idegen szervereken vannak, idegen kezekben.</p>", "<p>Your photos, documents and personal data sit on other people's servers, in other people's hands.</p>"),
("<h3>Korlátozott kontroll</h3>", "<h3>Limited control</h3>"),
("<p>Nem te döntöd el, milyen szolgáltatásokat használsz, és mikor szűnnek meg.</p>", "<p>You do not decide which services you get, or when they are switched off.</p>"),
('id="miert"', 'id="why"'),
("<h2>Miért válaszd a <span>saját szervered?</span></h2>", "<h2>Why choose <span>your own server?</span></h2>"),
("<p>Az előnyök, amik miatt megéri</p>", "<p>What makes it worth it</p>"),
("Teljes adatvédelem</h3>", "Privacy</h3>"),
("<p>Az adataid a te otthonodban maradnak, nem a Google vagy a Microsoft szerverein. A távoli mentésbe csak titkosítva kerülnek.</p>",
"<p>Your data stays in your home, not on Google's or Microsoft's servers. It goes into the remote backup only encrypted.</p>"),
("Egyszeri befektetés</h3>", "A one-off investment</h3>"),
("<p>A gép a tiéd, örökre. Nincs tárhely-előfizetés, ami évről évre drágul: az áramot fizeted, és ha kéred, az üzemeltetést.</p>",
"<p>The machine is yours, for good. No storage subscription that gets dearer every year: you pay for the electricity and, if you want it, for the management.</p>"),
("Korlátlan kapacitás</h3>", "Room to grow</h3>"),
("<p>Nincs 15GB-os limit. Bővítheted ahogy nőnek az igényeid - TB-okkal, ha kell.</p>", "<p>No 15 GB limit. Add space as your needs grow — terabytes, if you need them.</p>"),
("Teljes kontroll</h3>", "Full control</h3>"),
("<p>Te döntöd el, milyen szolgáltatásokat futtatsz. Te vagy a főnök, nem a szolgáltató.</p>", "<p>You decide which services you run. You are the boss, not the provider.</p>"),
('id="hogyan-mukodik"', 'id="how-it-works"'),
("<h2>Hogyan <span>működik?</span></h2>", "<h2>How it <span>works</span></h2>"),
("<p>5 egyszerű lépés az indulástól a működő rendszerig</p>", "<p>Five simple steps from the start to a working system</p>"),
("<h3>Egyeztetés</h3>", "<h3>A first talk</h3>"),
("<p>Megbeszéljük az igényeidet: milyen alkalmazásokra van szükséged, és mekkora tárhelyet szeretnél.</p>", "<p>We talk about what you need: which apps, and how much storage.</p>"),
("<h3>Hardver kiválasztás</h3>", "<h3>Choosing the hardware</h3>"),
("<p>Segítünk megtalálni a legjobb ár-érték arányú gépet, ami az igényeidnek megfelel.</p>", "<p>We help you find the machine with the best value for what you need.</p>"),
("<h3>Telepítés</h3>", "<h3>Installation</h3>"),
("<p>Feltelepítjük és bekonfiguráljuk az alkalmazásokat, a mentéseket, és a monitoringot.</p>", "<p>We install and set up the apps, the backups and the monitoring.</p>"),
("<h3>Átadás</h3>", "<h3>Handover</h3>"),
("<p>Bemutatjuk a rendszert, segítünk áthozni a meglévő adataidat, és válaszolunk minden kérdésedre.</p>", "<p>We show you the system, help you bring over your existing data, and answer all your questions.</p>"),
("<h3>Üzemeltetés</h3>", "<h3>Running it</h3>"),
("<p>Figyeljük a rendszert, frissítünk, mentéseket ellenőrzünk. Te csak használod.</p>", "<p>We watch the system, update it and check the backups. You just use it.</p>"),
('id="szolgaltatasok"', 'id="services"'),
("<h2>Mit kapsz <span>tőlünk?</span></h2>", "<h2>What you get <span>from us</span></h2>"),
("<p>Kulcsrakész megoldások, professzionális kivitelezéssel</p>", "<p>Turnkey solutions, done professionally</p>"),
("<h3>Szerver beszerzés és beüzemelés</h3>", "<h3>Buying and setting up the server</h3>"),
("<p>Segítünk kiválasztani az igényeidnek megfelelő hardvert, és mindent beállítunk.</p>", "<p>We help you choose the right hardware for your needs, and we set everything up.</p>"),
("<h3>Alkalmazás telepítés</h3>", "<h3>Installing apps</h3>"),
("<p>Nextcloud, Plex, Jellyfin, Home Assistant, és még sok más - amit csak szeretnél.</p>", "<p>Nextcloud, Plex, Jellyfin, Home Assistant and many more — whatever you want.</p>"),
("<h3>Biztonságos távoli elérés</h3>", "<h3>Safe remote access</h3>"),
("<p>Bárhonnan elérheted a szervereidet, biztonságosan, titkosított kapcsolaton keresztül.</p>", "<p>Reach your server from anywhere, safely, over an encrypted connection.</p>"),
("<h3>Automatikus mentések</h3>", "<h3>Automatic backups</h3>"),
("<p>Rendszeres, automatikus mentések három szinten: a dobozon, egy második meghajtón és titkosítva egy távoli tárhelyen.</p>",
"<p>Regular, automatic backups on three levels: on the box, on a second drive, and encrypted on remote storage.</p>"),
("<h3>Támogatás és karbantartás</h3>", "<h3>Support and maintenance</h3>"),
("<p>Ha bármi gond van, itt vagyunk. Rendszeres frissítések, proaktív monitoring.</p>", "<p>If anything goes wrong, we are here. Regular updates, proactive monitoring.</p>"),
('id="alkalmazasok"', 'id="apps"'),
("<h2>Telepíthető <span>Alkalmazások</span></h2>", "<h2>Apps to <span>install</span></h2>"),
("<p>Nyílt forráskódú szoftverek, amiket a saját szervereden futtathatsz</p>", "<p>Software you run on your own server, almost all of it open source</p>"),
("<h3>Fájlkezelés</h3>", "<h3>Files</h3>"),
("<h3>Fotók & Média</h3>", "<h3>Photos & Media</h3>"),
("<h3>Média Automatizáció</h3>", "<h3>Media automation</h3>"),
("<h3>Dokumentumok</h3>", "<h3>Documents</h3>"),
("<h3>Biztonság</h3>", "<h3>Security</h3>"),
("<h3>Otthon & Életmód</h3>", "<h3>Home & Lifestyle</h3>"),
("<h3>Utazás & Szabadidő</h3>", "<h3>Travel & Leisure</h3>"),
("<h3>Dashboard</h3>\n <p>Homepage", "<h3>Start pages</h3>\n <p>Homepage"),
("<h3>Játékok</h3>", "<h3>Games</h3>"),
("<h3>Fejlesztőknek</h3>", "<h3>For developers</h3>"),
("<strong>56</strong> alkalmazás — szinte mind <strong>nyílt forráskódú</strong>", "<strong>56</strong> apps — almost all of them <strong>open source</strong>"),
('<a href="/alkalmazasok" class="cta-button-secondary">', '<a href="/en/apps" class="cta-button-secondary">'),
("Összes alkalmazás megtekintése →", "See all apps →"),
('id="technologiak"', 'id="technology"'),
("<h2>Professzionális <span>Technológiák</span></h2>", "<h2>Professional <span>technology</span></h2>"),
("<p>Ipari szintű megoldások, otthoni környezetben</p>", "<p>Industry-grade tools, at home</p>"),
("<h3>Konténerek</h3>", "<h3>Containers</h3>"),
("<p>Docker alapú, izolált alkalmazás futtatás</p>", "<p>Each app isolated, on Docker</p>"),
("<h3>Vezérlőpult</h3>", "<h3>Dashboard</h3>"),
("<p>Magyar vagy angol kezelőfelület</p>", "<p>An interface in English or Hungarian</p>"),
("<p>Stabil virtualizációs alap</p>", "<p>A stable virtualisation base</p>"),
("<h3>Háromszintű mentés</h3>", "<h3>Three-level backups</h3>"),
("<p>A dobozon, második meghajtón, titkosítva távol</p>", "<p>On the box, on a second drive, encrypted far away</p>"),
("<h3>Hálózat</h3>", "<h3>Network</h3>"),
("<p>HTTPS, titkosított alagút, nyitott port nélkül</p>", "<p>HTTPS, an encrypted tunnel, no open port</p>"),
("<h3>Tárolás</h3>", "<h3>Storage</h3>"),
("<p>SSD + HDD, második meghajtó a mentéseknek</p>", "<p>SSD + HDD, a second drive for backups</p>"),
("<h3>Hardver</h3>", "<h3>Hardware</h3>"),
("<p>Mini PC-től rack szerverig</p>", "<p>From a mini PC to a rack server</p>"),
('Az igényeidhez igazítjuk — a <strong style="color: var(--text-1);">legegyszerűbbtől</strong> a <strong>legkomolyabbig</strong>',
'Matched to your needs — from the <strong style="color: var(--text-1);">simplest</strong> to the <strong>most serious</strong>'),
('<a href="/technologiak" class="cta-button-secondary">', '<a href="/en/technology" class="cta-button-secondary">'),
("Technológiákról bővebben →", "More about the technology →"),
('id="mentesek"', 'id="backups"'),
("<h2>Adataid <span>biztonságban</span></h2>", "<h2>Your data, <span>kept safe</span></h2>"),
("<p>A nagy felhőszolgáltatók milliókat költenek arra, hogy az adataid ne vesszenek el. De tudtad, hogy ugyanezt az otthoni szerveredről is elérheted?</p>",
"<p>The big cloud providers spend millions so that your data is not lost. Did you know you can get the same from your home server?</p>"),
("<p>A 3-2-1 mentési szabályt követve professzionális szintű adatbiztonságot építünk fel – egy katasztrófa után is marad egy titkosított másolat, távol az otthonodtól.</p>",
"<p>Following the 3-2-1 backup rule, we build professional-grade data safety – after a disaster there is still an encrypted copy, far from your home.</p>"),
('<a href="/biztonsagimentes" class="cta-button-secondary">', '<a href="/en/backups" class="cta-button-secondary">'),
("Mentési stratégia részletei →", "The backup strategy in detail →"),
("<h4>Három másolat</h4>", "<h4>Three copies</h4>"),
("<p>Eredeti + két biztonsági mentés</p>", "<p>The original + two backups</p>"),
("<h4>Két tárolótípus</h4>", "<h4>Two kinds of storage</h4>"),
("<p>Különböző eszközökön tárolva</p>", "<p>Kept on different devices</p>"),
("<h4>Egy távoli helyen</h4>", "<h4>One far away</h4>"),
("<p>Offsite backup a biztonság érdekében</p>", "<p>An off-site backup, to be safe</p>"),
('id="zart-teszt"', 'id="closed-test"'),
("<h2>Zárt <span>teszt</span></h2>", "<h2>Closed <span>test</span></h2>"),
("<p>Néhány magyar háztartással most kezdjük el élesben használni a rendszert</p>", "<p>We are starting to use the system for real with a few households in Hungary</p>"),
("Valódi használat</h3>", "Real use</h3>"),
("<p>Nem demó: a doboz nálad marad, és az igazi fényképeidet, dokumentumaidat teszed rá. Segítünk a telepítésben és az átállásban, utána te használod a mindennapokban.</p>",
"<p>Not a demo: the box stays with you, and you put your real photos and documents on it. We help with the installation and the move, and then you use it every day.</p>"),
("Visszajelzés</h3>", "Feedback</h3>"),
("<p>Cserébe azt kérjük, hogy mondd el, mi zavaró, mi érthetetlen, mi nem működik. Egy „ezt a gombot nem találtam\" ér nekünk a legtöbbet — abból tudjuk, mit kell kijavítani.</p>",
"<p>In return we ask you to tell us what annoys you, what is unclear and what does not work. An \"I couldn't find this button\" is worth the most to us — it tells us what to fix.</p>"),
("Ismert korlátok</h3>", "Known limits</h3>"),
("<p>Őszintén: a vezérlőpulthoz jelenleg <strong>egy közös háztartási jelszó</strong> tartozik, külön családtagi fiókok még nincsenek (az egyes alkalmazásoknak viszont saját felhasználóik lehetnek). A <strong>TV-re streamelés (DLNA)</strong> még nincs: egyelőre böngészőből és mobil appból megy a médiatár. És mivel ez teszt, számíts arra, hogy találsz hibát — ez a lényege.</p>",
"<p>Honestly: the Dashboard has <strong>one shared household password</strong> today; there are no separate accounts for family members yet (each app can have its own users, though). <strong>Streaming to the TV (DLNA)</strong> is not there yet: for now the media library plays in the browser and in mobile apps. And because this is a test, expect to find bugs — that is the point.</p>"),
("Ha ez érdekel, írj — <strong>nem kötelez semmire</strong>, először csak beszélgetünk", "If this interests you, write to us — <strong>it commits you to nothing</strong>, first we just talk"),
('<a href="/kapcsolat?tema=zart-teszt" class="cta-button">', '<a href="/en/contact?tema=zart-teszt" class="cta-button">'),
("Jelentkezem tesztelőnek →\n", "I want to be a tester →\n"),
('id="kapcsolat"', 'id="contact"'),
("<h2>Érdekel? <span>Beszéljünk!</span></h2>", "<h2>Interested? <span>Let's talk!</span></h2>"),
('<a href="/kapcsolat" class="contact-link">', '<a href="/en/contact" class="contact-link">'),
("Kapcsolatfelvételi űrlap</a>", "Contact form</a>"),
]
for old, new in HEAD + BODY:
s = sub1(s, old, new)
s = finish_en(s, "/", "/en/", None)
write("en/index.html", s)
print("written en/index.html")
@@ -0,0 +1,158 @@
# -*- coding: utf-8 -*-
"""/en/technology — the English twin of technologiak.html."""
import sys
sys.path.insert(0, __file__.rsplit("/", 1)[0])
from common import *
from en_common import *
s = en_head(read("technologiak.html"), "/technologiak", "/en/technology")
s = tr(s, [
("<title>Technológiák — Docker, vezérlőpult, Proxmox otthoni szerverhez | Felhom.eu</title>",
"<title>Technology — Docker, the Dashboard and Proxmox for a home server | Felhom.eu</title>"),
('content="Ipari szintű technológiák otthon: Docker konténerek, magyar vagy angol nyelvű vezérlőpult, Proxmox virtualizáció. HTTPS és titkosított alagút — nyitott port nélkül."',
'content="Industry-grade technology at home, in Hungary: Docker containers, a Dashboard in English or Hungarian, Proxmox virtualisation. HTTPS and an encrypted tunnel — with no open port."'),
('content="Docker, vezérlőpult, Proxmox, konténer, virtualizáció, reverse proxy, HTTPS, titkosított alagút, otthoni szerver technológia"',
'content="Docker, dashboard, Proxmox, container, virtualisation, reverse proxy, HTTPS, encrypted tunnel, home server technology"'),
('content="Professzionális technológiák otthoni szerverhez"', 'content="Professional technology for a home server"', 2),
('content="Docker, magyar vagy angol nyelvű vezérlőpult, Proxmox — ipari szintű megoldások otthoni környezetben."',
'content="Docker, a Dashboard in English or Hungarian, Proxmox — industry-grade tools at home."'),
('content="Docker, magyar vagy angol nyelvű vezérlőpult, Proxmox — ipari megoldások, otthoni környezetben."',
'content="Docker, a Dashboard in English or Hungarian, Proxmox — industry tools, at home."'),
('"headline": "Alkalmazott technológiák otthoni szerverekhez",', '"headline": "The technology behind a Felhom home server",'),
('"description": "Docker konténerek, magyar vagy angol nyelvű vezérlőpult és Proxmox virtualizáció otthoni szerverre.",',
'"description": "Docker containers, a Dashboard in English or Hungarian and Proxmox virtualisation for a home server.",'),
("<h1>Alkalmazott <span>Technológiák</span></h1>", "<h1>The <span>technology</span></h1>"),
("<p>Modern, bevált megoldások az otthoni szerverek üzemeltetéséhez. Íme, hogyan építjük fel a rendszereket.</p>",
"<p>Modern, proven tools for running a home server. This is how we build the system.</p>"),
("<h3>Tartalom</h3>", "<h3>Contents</h3>"),
('<li><a href="#kontenerek">Miért konténerek?</a></li>', '<li><a href="#containers">Why containers?</a></li>'),
('<li><a href="#single-node">A kezelőréteg (vezérlőpult)</a></li>', '<li><a href="#dashboard">The control layer (Dashboard)</a></li>'),
('<li><a href="#proxmox">Virtualizáció (Proxmox)</a></li>', '<li><a href="#proxmox">Virtualisation (Proxmox)</a></li>'),
('<li><a href="#halozat">Hálózat és biztonság</a></li>', '<li><a href="#network">Network and security</a></li>'),
('<li><a href="#tarolas">Tárolás</a></li>', '<li><a href="#storage">Storage</a></li>'),
('<li><a href="#hardver">Hardver konfigurációk</a></li>', '<li><a href="#hardware">Hardware set-ups</a></li>'),
('id="kontenerek"', 'id="containers"'), ('id="single-node"', 'id="dashboard"'), ('id="halozat"', 'id="network"'),
('id="tarolas"', 'id="storage"'), ('id="hardver"', 'id="hardware"'),
("<h2>Miért <span>konténerek?</span></h2>", "<h2>Why <span>containers?</span></h2>"),
("<p>A modern szerverüzemeltetés alapköve</p>", "<p>The cornerstone of running a modern server</p>"),
("Mi az a konténer?</h3>", "What is a container?</h3>"),
("<p>Képzeld el, hogy minden alkalmazás egy saját, elszigetelt „dobozban\" fut. Ez a doboz tartalmazza az alkalmazást és minden függőségét – így garantáltan ugyanúgy működik bárhol, bármilyen szerveren.</p>",
"<p>Imagine that every app runs in its own sealed \"box\". The box holds the app and everything it depends on – so it works the same way anywhere, on any server.</p>"),
("<p>A konténerek (Docker) forradalmasították a szerverüzemeltetést: egyszerűbb telepítés, könnyebb frissítés, és megbízhatóbb működés.</p>",
"<p>Containers (Docker) changed how servers are run: simpler installs, easier updates, and more reliable operation.</p>"),
("<p><strong>Analógia:</strong> Mintha minden alkalmazás a saját lakásában élne egy társasházban. Van saját bejárata, saját közműórája, és nem zavarja a szomszédokat – de mégis egy épületen osztoznak.</p>",
"<p><strong>An analogy:</strong> as if every app lived in its own flat in a block of flats. It has its own front door and its own meters, and it does not bother the neighbours – but they still share one building.</p>"),
("<h3>Hagyományos telepítés</h3>", "<h3>Traditional install</h3>"),
('<div class="subtitle">„Közvetlenül a rendszerre"</div>', '<div class="subtitle">"Straight onto the system"</div>'),
("<p>Az alkalmazások közvetlenül az operációs rendszerre települnek. Ütközhetnek egymással, nehéz eltávolítani őket.</p>",
"<p>The apps are installed straight onto the operating system. They can clash with each other, and they are hard to remove.</p>"),
("<li>Függőségek összeakadhatnak</li>", "<li>Dependencies can get tangled</li>"),
("<li>Nehéz reprodukálni más szerveren</li>", "<li>Hard to reproduce on another server</li>"),
("<li>Frissítés = kockázat</li>", "<li>Update = risk</li>"),
("<li>Eltávolítás „szemetet\" hagy</li>", "<li>Removing an app leaves \"rubbish\" behind</li>"),
("<h3>Konténerizált telepítés</h3>", "<h3>Container install</h3>"),
("<p>Minden alkalmazás elszigetelten fut. Tiszta, rendezett, könnyen kezelhető rendszer.</p>", "<p>Every app runs on its own. A clean, tidy system that is easy to manage.</p>"),
("<li>Teljesen izolált alkalmazások</li>", "<li>Fully isolated apps</li>"),
("<li>Egy parancs = telepítés</li>", "<li>One command = installed</li>"),
("<li>Egyszerű frissítés és visszaállás</li>", "<li>Simple updates, and a simple way back</li>"),
("<li>Bárhol reprodukálható</li>", "<li>Reproducible anywhere</li>"),
("<h2>A kezelőréteg: <span>a vezérlőpult</span></h2>", "<h2>The control layer: <span>the Dashboard</span></h2>"),
("<p>Minden Felhom doboz kezelőfelülete, magyarul vagy angolul</p>", "<p>The interface of every Felhom box, in English or Hungarian</p>"),
("Mit csinál a vezérlőpult?</h3>", "What does the Dashboard do?</h3>"),
("<p>A vezérlőpult minden Felhom dobozon ott van: ez a magyar vagy angol nyelvű kezelőfelület, amely a Proxmox alapra épülő rendszeredet vezérli. Egy kimondottan otthoni szerverekre tervezett felület, ahol pár kattintással telepíthetsz alkalmazásokat, és nem kell parancssorhoz nyúlnod — a háttérben a Docker és a Proxmox dolgozik helyetted.</p>",
"<p>The Dashboard is on every Felhom box: it is the interface, in English or Hungarian, that runs your system on its Proxmox base. It was designed for home servers: you install apps in a few clicks and never touch a command line — Docker and Proxmox do the work in the background.</p>"),
("<li>Magyar vagy angol webes felület – a háztartás választ, és nincs szükség parancssor ismeretre</li>", "<li>A web interface in English or Hungarian – the household chooses, and no command-line knowledge is needed</li>"),
("<li>Alkalmazás katalógus – 56 app telepíthető pár kattintással</li>", "<li>App catalog – 56 apps to install in a few clicks</li>"),
("<li>Erőforrás figyelés – memória és tárhely használat valós időben</li>", "<li>Resource view – memory and storage use as it happens</li>"),
("<li>Automatikus mentések – adatbázis és fájl backup, állapotfigyeléssel</li>", "<li>Automatic backups – databases and files, with their status watched</li>"),
("<li>Alkalmazás frissítések – csak kipróbált verzió, előtte friss mentés, és ha az új verzió nem indul, a doboz magától visszaáll az előzőre</li>",
"<li>App updates – only versions we have tried, a fresh backup first, and if the new version does not start, the box goes back to the previous one by itself</li>"),
("<li>Rendszer monitoring – folyamatos egészségfigyelés, riasztások</li>", "<li>System monitoring – health watched all the time, with alerts</li>"),
("<p><strong>Tökéletes:</strong> Otthoni média szerver, családi felhő, személyes projektek, kis irodai használat.</p>",
"<p><strong>Good for:</strong> a home media server, a family cloud, personal projects, a small office.</p>"),
("<h3>Egy Felhom doboz felépítése</h3>", "<h3>How a Felhom box is built</h3>"),
('<div class="layer-label">Kezelő + appok</div>', '<div class="layer-label">Control + apps</div>'),
('<div class="layer-name">Vezérlőpult + Alkalmazások</div>', '<div class="layer-name">Dashboard + Apps</div>'),
('<div class="layer-tech">Magyar vagy angol kezelőfelület · Nextcloud, Jellyfin, Immich…</div>', '<div class="layer-tech">An interface in English or Hungarian · Nextcloud, Jellyfin, Immich…</div>'),
('<div class="layer-label">Konténer motor</div>', '<div class="layer-label">Container engine</div>'),
('<div class="layer-tech">Az appok elszigetelt konténerekben futnak</div>', '<div class="layer-tech">The apps run in isolated containers</div>'),
('<div class="layer-label">Rendszerkörnyezet</div>', '<div class="layer-label">System environment</div>'),
('<div class="layer-name">Izolált rendszerkörnyezet</div>', '<div class="layer-name">Isolated system environment</div>'),
('<div class="layer-tech">A te dobozod — tisztán elkülönített rendszer</div>', '<div class="layer-tech">Your box — a cleanly separated system</div>'),
('<div class="layer-label">Alapréteg</div>', '<div class="layer-label">Base layer</div>'),
('<div class="layer-tech">Virtualizációs alap · a teljes doboz mentése, visszaállítás</div>', '<div class="layer-tech">Virtualisation base · backing up the whole box, restoring</div>'),
('<div class="layer-label">Hardver</div>', '<div class="layer-label">Hardware</div>'),
('<div class="layer-name">A szerver gépe</div>', '<div class="layer-name">The server machine</div>'),
('<div class="layer-tech">Mini PC, tower vagy szerver</div>', '<div class="layer-tech">Mini PC, tower or server</div>'),
("<h2>Az alapréteg: <span>Proxmox</span></h2>", "<h2>The base layer: <span>Proxmox</span></h2>"),
("<p>Erre a stabil alapra építünk minden Felhom gépet</p>", "<p>Every Felhom machine is built on this stable base</p>"),
("Mi az a Proxmox?</h3>", "What is Proxmox?</h3>"),
("<p>A Proxmox VE egy ingyenes, professzionális virtualizációs platform — ugyanaz a fajta technológia, amelyet adatközpontok és cégek használnak. Ez a Felhom rendszerek alapja: erre építünk minden gépet, és erre települ az izolált rendszerkörnyezet, amelyben az alkalmazásaid futnak.</p>",
"<p>Proxmox VE is a free, professional virtualisation platform — the same kind of technology data centres and companies use. It is the base of every Felhom system: we build every machine on it, and the isolated system environment your apps run in sits on top of it.</p>"),
("<strong>Az alap, nem opció:</strong> A Proxmox minden Felhom dobozon ott van. Nem kell róla tudnod és nem kell vele foglalkoznod — csendben a háttérben dolgozik, és ez teszi a rendszeredet biztonságossá és menthetővé.</p>",
"<strong>The base, not an option:</strong> Proxmox is on every Felhom box. You do not need to know about it or deal with it — it works quietly in the background, and it is what makes your system safe and able to be backed up.</p>"),
("Mit ad neked a Proxmox alap?</h3>", "What does the Proxmox base give you?</h3>"),
("<li><strong>Mentés frissítés előtt</strong> – egy alkalmazás frissítése előtt friss mentés készül, és ha az új verzió nem indul el, a doboz magától visszaállítja az előzőt</li>",
"<li><strong>A backup before each update</strong> – before an app is updated, a fresh backup is made, and if the new version does not start, the box puts the previous one back by itself</li>"),
("<li><strong>Az egész rendszer menthető</strong> – nem csak az adataid, hanem a teljes dobozod is biztonsági mentésbe kerül, és ebből visszaállítható</li>",
"<li><strong>The whole system is backed up</strong> – not only your data: the whole box goes into a backup too, and it can be restored from it</li>"),
("<li><strong>Tiszta elkülönítés</strong> – a kezelőréteg és az alkalmazásaid erősen elkülönítve futnak, így stabilabb és biztonságosabb a rendszer</li>",
"<li><strong>Clean separation</strong> – the control layer and your apps run well apart, which makes the system more stable and safer</li>"),
("<li><strong>Gyors, megismételhető telepítés</strong> – egy új doboz beüzemelése gyors és kiszámítható, mindig ugyanúgy</li>",
"<li><strong>A fast, repeatable install</strong> – setting up a new box is quick and predictable, the same way every time</li>"),
("<p><strong>Egy alap, egy feladat:</strong> a Felhom dobozon a Proxmox egyetlen elkülönített rendszerkörnyezetet futtat — azt, amelyben az alkalmazásaid dolgoznak.</p>",
"<p><strong>One base, one job:</strong> on a Felhom box, Proxmox runs a single isolated system environment — the one your apps work in.</p>"),
("<h2>Hálózat és <span>biztonság</span></h2>", "<h2>Network and <span>security</span></h2>"),
("<p>Hogyan éred el a szervered biztonságosan, bárhonnan?</p>", "<p>How do you reach your server safely, from anywhere?</p>"),
("<p>A reverse proxy egy „kapus\", aki fogadja a beérkező kéréseket és továbbítja a megfelelő alkalmazáshoz. Emellett kezeli a HTTPS titkosítást is.</p>",
"<p>A reverse proxy is a \"doorkeeper\" that takes incoming requests and passes them to the right app. It also handles the HTTPS encryption.</p>"),
('<div class="node-label">Alkalmazás</div>', '<div class="node-label">App</div>'),
("<li><strong>Traefik</strong> – minden alkalmazás a saját aldomainjén, HTTPS-sel</li>", "<li><strong>Traefik</strong> – every app on its own subdomain, over HTTPS</li>"),
("<li><strong>Titkosított alagút</strong> – a doboz maga épít kapcsolatot kifelé, így otthon nem kell portot nyitni</li>",
"<li><strong>Encrypted tunnel</strong> – the box itself builds the connection outwards, so no port has to be opened at home</li>"),
("Védelem</h3>", "Protection</h3>"),
("<li><strong>Nincs nyitott port</strong> – kívülről csak a titkosított alagúton át érhető el a doboz</li>", "<li><strong>No open port</strong> – from outside, the box is reached only through the encrypted tunnel</li>"),
("<li><strong>Saját bejelentkezés</strong> – minden alkalmazásnak saját fiókjai vannak, a vezérlőpultnak jelszava</li>", "<li><strong>Own sign-in</strong> – every app has its own accounts, and the Dashboard has a password</li>"),
("<li><strong>Családi kapu</strong> – egyes alkalmazások elé a családtagok saját belépése kerül, idegen el sem jut az alkalmazásig</li>",
"<li><strong>Family gate</strong> – some apps sit behind each family member's own sign-in, so a stranger never even reaches the app</li>"),
("<li><strong>Biztonsági frissítések</strong> – a doboz rendszere magától megkapja őket</li>", "<li><strong>Security updates</strong> – the box's system gets them by itself</li>"),
("<h2>Tárolás és <span>adatvédelem</span></h2>", "<h2>Storage and <span>data safety</span></h2>"),
("<p>Hol és hogyan tárold az adataidat?</p>", "<p>Where and how should you keep your data?</p>"),
('<div class="subtitle">Gyors tárolás</div>', '<div class="subtitle">Fast storage</div>'),
("<li>Operációs rendszer</li>", "<li>Operating system</li>"),
("<li>Adatbázisok</li>", "<li>Databases</li>"),
("<li>Alkalmazások</li>", "<li>Apps</li>"),
('<div class="subtitle">Nagy kapacitás</div>', '<div class="subtitle">Large capacity</div>'),
("<li>Média könyvtár</li>", "<li>Media library</li>"),
("<li>Fotó archívum</li>", "<li>Photo archive</li>"),
("<li>Mentések</li>", "<li>Backups</li>"),
("Háromszintű mentés – így védjük az adataidat</h3>", "Backups on three levels – how we protect your data</h3>"),
("<p>A Felhom dobozon minden éjjel készül egy <strong>mentés a dobozon</strong>, egy <strong>másodpéldány egy második meghajtóra</strong>, és egy <strong>titkosított másolat egy távoli tárhelyre</strong>. A szintek együtt védenek: a helyi gyors, a távoli túléli azt is, ha a doboznak fizikailag baja esik.</p>",
"<p>Every night a Felhom box makes a <strong>backup on the box</strong>, a <strong>second copy on a second drive</strong>, and an <strong>encrypted copy on remote storage</strong>. The levels protect together: the local one is fast, and the remote one survives even if the box itself is physically harmed.</p>"),
("<strong>A több lemez önmagában nem mentés!</strong> A RAID-szerű megoldások a hardver meghibásodás ellen védenek, de nem a véletlen törlés vagy a zsarolóvírus ellen. Ezért nálunk a mentés mindig külön szinten van, nem a lemezekben.</p>",
"<strong>More disks are not a backup on their own!</strong> RAID-like set-ups protect against hardware failure, but not against an accidental delete or ransomware. That is why our backups always sit on a separate level, not in the disks.</p>"),
("<h2>Hardver <span>konfigurációk</span></h2>", "<h2>Hardware <span>set-ups</span></h2>"),
("<p>Milyen gépre van szükséged? Néhány példa konfiguráció</p>", "<p>Which machine do you need? A few example set-ups</p>"),
('<div class="hw-subtitle">Kezdő / Otthoni</div>', '<div class="hw-subtitle">Starter / Home</div>'),
("<p>Kompakt, halk, energiatakarékos megoldás.</p>", "<p>Compact, quiet and energy-saving.</p>"),
('<span class="spec-label">Fogyasztás</span>', '<span class="spec-label">Power use</span>', 2),
('<div class="label">Becsült költség</div>', '<div class="label">Estimated cost</div>', 2),
("<h3>Tower szerver</h3>", "<h3>Tower server</h3>"),
('<div class="hw-subtitle">Haladó / Családi</div>', '<div class="hw-subtitle">Advanced / Family</div>'),
("<p>Nagyobb teljesítmény és tárolókapacitás.</p>", "<p>More power and more storage.</p>"),
('<span class="spec-label">Tárolás</span>', '<span class="spec-label">Storage</span>', 2),
("<h3>Rack szerver</h3>", "<h3>Rack server</h3>"),
('<div class="hw-subtitle">Üzleti / igény szerint</div>', '<div class="hw-subtitle">Business / on request</div>'),
("<p>Ipari minőség, nagy megbízhatóság.</p>", "<p>Industrial quality, high reliability.</p>"),
('<div class="label">Becsült költség (használt)</div>', '<div class="label">Estimated cost (used)</div>'),
("Melyiket válaszd?</h3>", "Which one should you choose?</h3>"),
("<p><strong>Tanácsunk:</strong> Kezdj kicsiben! Egy jó mini PC elegendő a legtöbb otthoni igényhez, és később bővítheted. A rack szerver üzleti / igény szerinti konfiguráció — otthonra ritkán szükséges.</p>",
"<p><strong>Our advice:</strong> start small! A good mini PC is enough for most homes, and you can expand it later. A rack server is a business / on-request set-up — a home rarely needs one.</p>"),
("<h2>Nem vagy biztos, melyik <span>megoldás neked való?</span></h2>", "<h2>Not sure which <span>set-up is right for you?</span></h2>"),
("<p>Beszéljük át az igényeidet, és segítünk megtalálni a tökéletes konfigurációt.</p>", "<p>Let's talk through what you need, and we will help you find the right set-up.</p>"),
('<a href="/kapcsolat" class="cta-button">Kapcsolatfelvétel →</a>', '<a href="/en/contact" class="cta-button">Get in touch →</a>'),
('<a href="/alkalmazasok" class="cta-button-secondary">Alkalmazások böngészése →</a>', '<a href="/en/apps" class="cta-button-secondary">Browse the apps →</a>'),
])
s = finish_en(s, "/technologiak", "/en/technology", "/en/technology")
write("en/technology.html", s)
print("written en/technology.html")
@@ -0,0 +1,121 @@
# -*- coding: utf-8 -*-
"""Part A — the Hungarian pages: every claim the claim table marks FIX or CUT, plus the nav switch and hreflang.
Starts from HEAD each run (idempotent). Every replacement asserts it matched exactly once."""
import html, json, re, sys
sys.path.insert(0, __file__.rsplit("/", 1)[0])
from common import *
APPS = 56 # measured 2026-10-08: 58 catalog templates minus plant-it (abandoned) and wger (hidden)
# ------------------------------------------------------------------ index.html
s = head_text("index.html")
s = sub1(s, "53 telepíthető nyílt forráskódú alkalmazás, kétszintű automatikus mentés, magyar nyelvű vezérlőpult.",
"%d telepíthető alkalmazás, háromszintű automatikus mentés, magyar vagy angol nyelvű vezérlőpult." % APPS)
s = sub1(s, '"knowsLanguage": "hu",', '"knowsLanguage": ["hu", "en"],')
s = sub1(s, """Kétszintű automatikus mentés</h3>
<p>Minden éjjel készül egy helyi másodpéldány egy második meghajtóra, és egy titkosított másolat egy távoli tárhelyre. A megosztott mappáid is benne vannak mindkettőben.</p>""",
"""Háromszintű automatikus mentés</h3>
<p>Minden éjjel készül egy mentés a dobozon, egy másodpéldány egy második meghajtóra (ha van), és egy titkosított másolat egy távoli tárhelyre. A megosztott mappáid a második meghajtón és a távoli másolatban is benne vannak.</p>""")
s = sub1(s, """53 telepíthető alkalmazás</h3>
<p>Egy magyar nyelvű vezérlőpultból telepíthetők, pár kattintással — parancssor nélkül. Mind nyílt forráskódú, és mind a te gépeden fut.</p>""",
"""%d telepíthető alkalmazás</h3>
<p>A vezérlőpultból telepíthetők, pár kattintással — parancssor nélkül. Szinte mind nyílt forráskódú (a kivételeket az alkalmazások oldalán jelöljük), és mind a te gépeden fut.</p>""" % APPS)
s = sub1(s, """Távoli felügyelet, automatikus frissítés</h3>
<p>Figyeljük a doboz állapotát, és a vezérlőpult magától frissül a legutóbbi kiadásra — neked nem kell csinálnod semmit. Ha valami elromlik, mi látjuk.</p>
</div>""",
"""Távoli felügyelet, automatikus frissítés</h3>
<p>Figyeljük a doboz állapotát. A vezérlőpult magától frissül a legutóbbi kiadásra, és a doboz rendszere is megkapja a biztonsági frissítéseket — neked nem kell csinálnod semmit. Ha valami elromlik, mi látjuk.</p>
</div>
<div class="why-card">
<h3><svg class="ico-lg" aria-hidden="true"><use href="/assets/icons.svg?v=1#i-rotate-cw"/></svg> Óvatos alkalmazásfrissítés</h3>
<p>Egy alkalmazás új verziója csak akkor jut el hozzád, ha előbb kipróbáltuk. Frissítés előtt friss mentés készül, és ha az új verzió nem indul el rendesen, a doboz magától visszaállítja az előzőt, az adataiddal együtt.</p>
</div>
<div class="why-card">
<h3><svg class="ico-lg" aria-hidden="true"><use href="/assets/icons.svg?v=1#i-globe"/></svg> Magyarul vagy angolul</h3>
<p>A vezérlőpult magyarul és angolul is használható. A nyelvet a háztartás választja, és a dobozról érkező e-mailek is ezt a nyelvet követik.</p>
</div>""")
s = sub1(s, "<p>Egy törölt fájl visszahozható a mentésből. Hardverhiba esetén az egész rendszer visszaállítható egy új gépre, a titkosított távoli mentésből.</p>",
"<p>Egy törölt fájl visszahozható a mentésből. Ha maga a doboz megy tönkre, az újratelepített rendszerre a titkosított távoli mentésből visszahozhatók az alkalmazásaid adatai — a helyreállítási kóddal.</p>")
s = sub1(s, "<p>Az adataid a te otthonodban maradnak. Senki más nem fér hozzájuk - sem a Google, sem a Microsoft, sem senki más.</p>",
"<p>Az adataid a te otthonodban maradnak, nem a Google vagy a Microsoft szerverein. A távoli mentésbe csak titkosítva kerülnek.</p>")
s = sub1(s, """Egyszeri befektetés</h3>
<p>Nincs havi előfizetés, nincs rejtett költség. A szerver a tiéd, örökre. Csak az áramért fizetsz.</p>""",
"""Egyszeri befektetés</h3>
<p>A gép a tiéd, örökre. Nincs tárhely-előfizetés, ami évről évre drágul: az áramot fizeted, és ha kéred, az üzemeltetést.</p>""")
s = sub1(s, "<p>Soha többé nem veszíted el az adataidat - rendszeres, automatikus mentések.</p>",
"<p>Rendszeres, automatikus mentések három szinten: a dobozon, egy második meghajtón és titkosítva egy távoli tárhelyen.</p>")
s = sub1(s, "<p>Paperless-ngx, BookStack, Outline</p>", "<p>Paperless-ngx, Docmost, BookStack, Outline</p>")
s = sub1(s, "<p>Tandoor, Plant-it, wger, Actual Budget</p>", "<p>Tandoor, Mealie, Homebox, Actual Budget</p>")
s = sub1(s, "<p>AdventureLog, Wanderer, Cal.com</p>", "<p>AdventureLog, Wanderer, Dawarich, Cal.com</p>")
s = sub1(s, "<strong>53</strong> alkalmazás — mind <strong>100% nyílt forráskódú</strong>",
"<strong>%d</strong> alkalmazás — szinte mind <strong>nyílt forráskódú</strong>" % APPS)
s = sub1(s, "<p>Magyar nyelvű kezelőfelület</p>", "<p>Magyar vagy angol kezelőfelület</p>")
s = sub1(s, """<h3>Kétszintű mentés</h3>
<p>Helyi másodpéldány + titkosított távoli</p>""",
"""<h3>Háromszintű mentés</h3>
<p>A dobozon, második meghajtón, titkosítva távol</p>""")
s = sub1(s, "<p>SSL, tűzfal, VPN védelem</p>", "<p>HTTPS, titkosított alagút, nyitott port nélkül</p>")
s = sub1(s, "<p>SSD + HDD, RAID védelem</p>", "<p>SSD + HDD, második meghajtó a mentéseknek</p>")
s = sub1(s, "<p>A 3-2-1 mentési szabályt követve professzionális szintű adatbiztonságot építünk fel – semmi sem vész el, még katasztrófa esetén sem.</p>",
"<p>A 3-2-1 mentési szabályt követve professzionális szintű adatbiztonságot építünk fel – egy katasztrófa után is marad egy titkosított másolat, távol az otthonodtól.</p>")
s = sub1(s, "A <strong>TV-re streamelés hamarosan</strong> jön, egyelőre böngészőből és mobil appból megy a médiatár.",
"A <strong>TV-re streamelés (DLNA)</strong> még nincs: egyelőre böngészőből és mobil appból megy a médiatár.")
s = set_alternates(s, "/", "/en/")
s = set_nav(s, "hu", None, "/en/")
write("index.html", s)
# ------------------------------------------------------------------ technologiak.html
s = head_text("technologiak.html")
s = s[: s.index("</html>") + len("</html>")] # a stray file-dump summary sat after </html>, visible on the page
s = sub1(s, "Docker konténerek, magyar nyelvű vezérlőpult, Proxmox virtualizáció. SSL, tűzfal, VPN — professzionális biztonság.",
"Docker konténerek, magyar vagy angol nyelvű vezérlőpult, Proxmox virtualizáció. HTTPS és titkosított alagút — nyitott port nélkül.")
s = sub1(s, "reverse proxy, SSL, tűzfal, VPN, otthoni szerver technológia", "reverse proxy, HTTPS, titkosított alagút, otthoni szerver technológia")
s = sub1(s, "Docker, magyar nyelvű vezérlőpult, Proxmox — ipari szintű megoldások otthoni környezetben.",
"Docker, magyar vagy angol nyelvű vezérlőpult, Proxmox — ipari szintű megoldások otthoni környezetben.")
s = sub1(s, "Docker, magyar nyelvű vezérlőpult, Proxmox — ipari megoldások, otthoni környezetben.",
"Docker, magyar vagy angol nyelvű vezérlőpult, Proxmox — ipari megoldások, otthoni környezetben.")
s = sub1(s, '"description": "Docker konténerek, magyar nyelvű vezérlőpult és Proxmox virtualizáció otthoni szerverre.",',
'"description": "Docker konténerek, magyar vagy angol nyelvű vezérlőpult és Proxmox virtualizáció otthoni szerverre.",')
s = sub1(s, '<div class="subtitle">Docker / Podman</div>', '<div class="subtitle">Docker</div>')
s = sub1(s, "<p>Minden Felhom doboz magyar nyelvű kezelőfelülete</p>", "<p>Minden Felhom doboz kezelőfelülete, magyarul vagy angolul</p>")
s = sub1(s, "ez a magyar nyelvű kezelőfelület, amely a Proxmox alapra épülő rendszeredet vezérli.",
"ez a magyar vagy angol nyelvű kezelőfelület, amely a Proxmox alapra épülő rendszeredet vezérli.")
s = sub1(s, "<li>Magyar nyelvű webes felület – nincs szükség parancssor ismeretre</li>",
"<li>Magyar vagy angol webes felület – a háztartás választ, és nincs szükség parancssor ismeretre</li>")
s = sub1(s, "<li>Alkalmazás katalógus – 53 app telepíthető pár kattintással</li>",
"<li>Alkalmazás katalógus – %d app telepíthető pár kattintással</li>" % APPS)
s = sub1(s, "<li>Alkalmazás frissítések – egy kattintással, naplózással</li>",
"<li>Alkalmazás frissítések – csak kipróbált verzió, előtte friss mentés, és ha az új verzió nem indul, a doboz magától visszaáll az előzőre</li>")
s = sub1(s, '<div class="layer-tech">Magyar kezelőfelület · Nextcloud, Jellyfin, Immich…</div>',
'<div class="layer-tech">Magyar vagy angol kezelőfelület · Nextcloud, Jellyfin, Immich…</div>')
s = sub1(s, '<div class="layer-tech">Virtualizációs alap · pillanatkép, mentés, visszaállítás</div>',
'<div class="layer-tech">Virtualizációs alap · a teljes doboz mentése, visszaállítás</div>')
s = sub1(s, "<li><strong>Pillanatkép minden változtatás előtt</strong> – frissítés vagy beállítás előtt a rendszer pillanatképet készít, így bármi gond esetén pillanatok alatt visszaállunk az előző állapotra</li>",
"<li><strong>Mentés frissítés előtt</strong> – egy alkalmazás frissítése előtt friss mentés készül, és ha az új verzió nem indul el, a doboz magától visszaállítja az előzőt</li>")
s = sub1(s, "<li><strong>Az egész rendszer menthető</strong> – nem csak az adataid, hanem a teljes dobozod biztonsági mentésbe tehető, és szükség esetén egy új gépre is visszaállítható</li>",
"<li><strong>Az egész rendszer menthető</strong> – nem csak az adataid, hanem a teljes dobozod is biztonsági mentésbe kerül, és ebből visszaállítható</li>")
s = re.sub(r"\n\s*<div class=\"[^\"]*\">\s*\n\s*<p><strong>És ha többre van szükséged…</strong>.*?</p>\s*\n\s*</div>", "", s, count=0, flags=re.S) \
if False else sub1(s, "<p><strong>És ha többre van szükséged…</strong> ugyanez az alap teszi lehetővé a haladó igényeket is: Windows-os szoftver vagy hálózati szolgáltatás (pl. pfSense/OPNsense) külön virtuális gépben, tesztkörnyezetek, vagy egy app extra erős elkülönítése — kérésre.</p>",
"<p><strong>Egy alap, egy feladat:</strong> a Felhom dobozon a Proxmox egyetlen elkülönített rendszerkörnyezetet futtat — azt, amelyben az alkalmazásaid dolgoznak.</p>")
s = sub1(s, '<div class="node-tech">Traefik / Nginx</div>', '<div class="node-tech">Traefik</div>')
s = sub1(s, """ <li><strong>Traefik</strong> – automatikus SSL, dinamikus konfiguráció</li>
<li><strong>Nginx Proxy Manager</strong> – egyszerű webes felület</li>
<li><strong>Caddy</strong> – minimalista, automatikus HTTPS</li>""",
""" <li><strong>Traefik</strong> – minden alkalmazás a saját aldomainjén, HTTPS-sel</li>
<li><strong>Titkosított alagút</strong> – a doboz maga épít kapcsolatot kifelé, így otthon nem kell portot nyitni</li>""")
s = sub1(s, """ <li><strong>UFW tűzfal</strong> – csak a szükséges portok nyitva</li>
<li><strong>Fail2ban</strong> – brute-force támadások blokkolása</li>
<li><strong>Geo-blocking</strong> – kérések szűrése földrajzi hely alapján</li>
<li><strong>WireGuard VPN</strong> – biztonságos, titkosított távoli elérés</li>""",
""" <li><strong>Nincs nyitott port</strong> – kívülről csak a titkosított alagúton át érhető el a doboz</li>
<li><strong>Saját bejelentkezés</strong> – minden alkalmazásnak saját fiókjai vannak, a vezérlőpultnak jelszava</li>
<li><strong>Családi kapu</strong> – egyes alkalmazások elé a családtagok saját belépése kerül, idegen el sem jut az alkalmazásig</li>
<li><strong>Biztonsági frissítések</strong> – a doboz rendszere magától megkapja őket</li>""")
s = sub1(s, "Tűzfal és védelem</h3>", "Védelem</h3>")
s = sub1(s, "Kétszintű mentés – így védjük az adataidat</h3>", "Háromszintű mentés – így védjük az adataidat</h3>")
s = sub1(s, "<p>A Felhom dobozon minden éjjel készül egy <strong>helyi másodpéldány</strong> egy második meghajtóra, és egy <strong>titkosított másolat egy távoli tárhelyre</strong>. A két szint együtt véd: a helyi gyors, a távoli túléli azt is, ha a doboznak fizikailag baja esik.</p>",
"<p>A Felhom dobozon minden éjjel készül egy <strong>mentés a dobozon</strong>, egy <strong>másodpéldány egy második meghajtóra</strong>, és egy <strong>titkosított másolat egy távoli tárhelyre</strong>. A szintek együtt védenek: a helyi gyors, a távoli túléli azt is, ha a doboznak fizikailag baja esik.</p>")
s = set_alternates(s, "/technologiak", "/en/technology")
s = set_nav(s, "hu", "/technologiak", "/en/technology")
write("technologiak.html", s)
@@ -0,0 +1,125 @@
# -*- coding: utf-8 -*-
"""Part A, second half: biztonsagimentes, gyik, kapcsolat, letoltes, 404 (Hungarian)."""
import html, json, re, sys
sys.path.insert(0, __file__.rsplit("/", 1)[0])
from common import *
APPS = 56
# ------------------------------------------------------------------ biztonsagimentes.html
s = head_text("biztonsagimentes.html")
s = sub1(s, "Professzionális mentési stratégiával gondoskodunk róla, hogy soha ne veszíts el semmit.",
"Professzionális mentési stratégiával gondoskodunk róla, hogy egy hiba vagy baleset után is legyen honnan visszahozni.")
s = sub1(s, "<p>Kritikus adatok (dokumentumok, fotók) általában elférnek néhány száz GB-on, ami havi pár száz forint.</p>",
"<p>A tárhely díja a mentett adatmennyiségtől függ. A Felhom dobozon ezt a megoldást használjuk.</p>")
s = sub1(s, "<p>A szerver automatikusan, ütemezetten készít mentéseket. Nem kell semmire sem emlékezned – a rendszer gondoskodik róla.</p>",
"<p>A szerver minden éjjel magától készít mentéseket, három szinten: <strong>a dobozon</strong>, egy <strong>második meghajtón</strong> (ha van), és <strong>titkosítva egy távoli tárhelyen</strong>. Nem kell semmire sem emlékezned – a rendszer gondoskodik róla.</p>")
s = sub1(s, "a teljes doboz biztonsági mentésbe tehető és szükség esetén egy új gépre is visszaállítható (titkosított offsite mentésből). Ez teszi lehetővé, hogy egy hardverhiba után gyorsan újrainduljon minden.</p>",
"a teljes doboz is biztonsági mentésbe kerül, és ebből visszaállítható. Ha maga a doboz megy tönkre, az újratelepített rendszerre a titkosított távoli mentésből visszahozhatók az alkalmazásaid adatai — a helyreállítási kóddal.</p>")
s = sub1(s, "<p><strong>Személyre szabjuk:</strong> Minden ügyfélnél felmérjük a lehetőségeket (van-e haver szerver, iroda, felhő preferencia), és ennek megfelelően állítjuk be az offsite mentést.</p>",
"<p><strong>A távoli mentés helye:</strong> a Felhom dobozon a távoli másolat egy általunk biztosított tárhelyre kerül, titkosítva. A „haver rendszer” és az iroda általános lehetőségek a 3-2-1 szabályhoz — ezeket ma nem állítjuk be.</p>")
s = sub1(s, "Automatikus backup, offsite mentés, titkosított felhő tárolás. Adataid soha nem vesznek el.",
"Automatikus backup három szinten: a dobozon, egy második meghajtón és titkosítva egy távoli tárhelyen.")
s = sub1(s, "Professzionális szintű adatbiztonság otthon. Automatikus backup, offsite mentés — semmi nem vész el.",
"Professzionális szintű adatbiztonság otthon. Automatikus backup, és egy titkosított másolat távol is.")
s = set_alternates(s, "/biztonsagimentes", "/en/backups")
s = set_nav(s, "hu", "/biztonsagimentes", "/en/backups")
write("biztonsagimentes.html", s)
# ------------------------------------------------------------------ gyik.html (visible answers; JSON-LD rebuilt from them)
s = head_text("gyik.html")
R = [
("Nálunk több mint 45 alkalmazásból válogathatsz, és bármi egyedi igényt is megoldunk.</p>",
"Nálunk %d alkalmazásból válogathatsz, és ha valami hiányzik, megnézzük, felvehető-e.</p>" % APPS),
("<p>Több mint <strong>45 alkalmazást</strong> kínálunk — a teljes listát",
"<p><strong>%d alkalmazást</strong> kínálunk — a teljes listát" % APPS),
("<p>Ha valami nincs a listán, kérdezz — a legtöbb nyílt forráskódú alkalmazás telepíthető.</p>",
"<p>Ha valami nincs a listán, kérdezz — megnézzük, felvehető-e a katalógusba.</p>"),
("<p><strong>Igen, bármikor!</strong> A rendszer úgy van felépítve, hogy új alkalmazások könnyedén hozzáadhatók. Szólj, mit szeretnél, és mi telepítjük és bekonfiguráljuk neked.</p>",
"<p><strong>Igen, bármikor!</strong> A vezérlőpultból bármelyik alkalmazás telepíthető, pár kattintással. Ha elakadsz, szólj, és segítünk beállítani.</p>"),
("<p>Igen! Biztonságos távoli elérést biztosítunk, titkosított VPN kapcsolaton vagy reverse proxy-n keresztül. Ez azt jelenti,",
"<p>Igen! A doboz egy titkosított alagúton át, HTTPS-sel érhető el, külön alkalmazás nélkül. Ez azt jelenti,"),
("több rétegű biztonsági védelmet kapnak: tűzfal, automatikus SSL/TLS titkosítás (HTTPS), rendszeres szoftverfrissítések, és biztonságos hozzáférés-kezelés. A szervered a te otthonodon belül van — kívülről csak az általunk beállított, titkosított csatornákon keresztül érhető el.</p>",
"több rétegű védelmet kapnak: automatikus HTTPS titkosítás, rendszeres biztonsági frissítések, saját bejelentkezés minden alkalmazásban, és egyes alkalmazások előtt családi kapu. A szervered a te otthonodon belül van — otthon nem nyitunk portot, kívülről csak egy titkosított alagúton keresztül érhető el.</p>"),
("<p>Ha ez számodra nem elfogadható, van lehetőség <strong>„önálló\" modellre</strong>: ilyenkor a telepítés után a távoli hozzáférést eltávolítjuk, és a jövőbeli támogatás személyes jelenléttel történik. Ilyenkor a mentési kulcsokat kizárólag te ismered.</p>",
"<p>A távoli mentésed kulcsát viszont <strong>csak te ismered</strong>: a helyreállítási kód egyszer jelenik meg, a te képernyődön, és nálunk nem marad belőle másolat. A távoli mentést így mi sem tudjuk elolvasni.</p>"),
("nincs reklámcélú profilozás, és az adataid soha nem hagyják el a hálózatodat.</p>",
"nincs reklámcélú profilozás, és az adataid csak titkosítva hagyják el az otthonodat (a távoli mentés).</p>"),
("A rendszeres frissítések és a megfelelő tűzfal konfiguráció minimalizálja a kockázatot.</p>",
"A rendszeres frissítések, és hogy otthon nincs nyitott port, csökkentik a kockázatot.</p>"),
("<p>A mentések titkosítva vannak és külön helyen (offsite) is tárolódnak — tehát még egy zsarolóvírus támadás esetén is vissza tudjuk állítani az adataidat a legutolsó mentésből.</p>",
"<p>A mentéseidből egy titkosított másolat külön helyen (offsite) is tárolódik — így egy zsarolóvírus-támadás után is van egy távol tárolt másolat.</p>"),
("<p>A <strong>3-2-1 mentési szabályt</strong> követjük: 3 másolat az adatokról, 2 különböző tárolón, 1 távoli helyen. Ez iparági szabvány, amit a legnagyobb cégek is alkalmaznak.</p>",
"<p>A <strong>3-2-1 mentési szabályt</strong> követjük: 3 másolat az adatokról, 2 különböző tárolón, 1 távoli helyen. Ez iparági szabvány, amit a legnagyobb cégek is alkalmaznak. A dobozon ez három szint: egy mentés a dobozon, egy másodpéldány egy második meghajtón, és egy titkosított másolat egy távoli tárhelyen.</p>"),
("<p>Ezért vannak a mentések! A helyi és offsite biztonsági mentésekből az adatok visszaállíthatók egy új lemezre. A monitoring rendszerünk figyeli a lemezhasználatot, és előre figyelmeztet, ha probléma van.</p>",
"<p>Ezért vannak a mentések! A második meghajtón és a távoli tárhelyen lévő másolatokból az alkalmazásaid adatai visszahozhatók. A doboz figyeli a lemezek állapotát és a szabad helyet, és előre figyelmeztet, ha probléma van.</p>"),
("\n <p>Nagyobb rendszereknél RAID konfigurációt is alkalmazhatunk, ami azt jelenti, hogy egy lemez meghibásodása esetén a rendszer adatvesztés nélkül tovább működik.</p>", ""),
("<p>Az offsite (távoli) backup azt jelenti, hogy az adataidról egy másolat fizikailag más helyen is megvan — például egy ismerős szerverén, az irodádban, vagy titkosítva egy felhő tárhelyen. Ha otthon bármi katasztrófa történne (tűz, lopás, vízkár), az adataid akkor is biztonságban vannak.</p>",
"<p>Az offsite (távoli) backup azt jelenti, hogy az adataidról egy másolat fizikailag más helyen is megvan. A Felhom dobozon ez egy általunk biztosított tárhely, ahova a másolat titkosítva kerül. Ha otthon bármi katasztrófa történne (tűz, lopás, vízkár), a távoli másolat akkor is megmarad.</p>"),
("<p>Az alkalmazások konténerekben futnak, ami megkönnyíti a frissítést. Mi figyelemmel kísérjük az új verziókat, és ha szükséges, frissítjük az alkalmazásaidat — tesztelés után, hogy ne okozzon gondot.</p>",
"<p>Az alkalmazások konténerekben futnak, ami megkönnyíti a frissítést. Mi figyelemmel kísérjük az új verziókat, és egy új verzió csak akkor jut el hozzád, ha előbb kipróbáltuk. A doboz rendszere a biztonsági frissítéseket magától megkapja.</p>"),
("<p>Minden frissítés előtt automatikus mentés és rendszer-pillanatkép készül (a Proxmox alapnak köszönhetően), így ha bármi nem stimmel, gyorsan visszaállíthatjuk az előző állapotot.</p>",
"<p>Minden alkalmazásfrissítés előtt friss mentés készül, és ha az új verzió nem indul el rendesen, a doboz magától visszaállítja az előzőt, az adataiddal együtt.</p>"),
("<p>A szerver és az összes adat a tiéd — mindig is az volt. Ha úgy döntesz, hogy nem kéred tovább a szolgáltatást, a rendszer tovább működik. Egyszerűen nem kapod a frissítéseket, a monitoring-ot, és a támogatást.</p>",
"<p>A szerver és az összes adat a tiéd — mindig is az volt. Ha úgy döntesz, hogy nem kéred tovább a szolgáltatást, a doboz az otthoni hálózaton tovább működik, de nem kapod a frissítéseket, a felügyeletet és a támogatást. A távoli elérés és a távoli mentés a szolgáltatás része: hogy mi lesz velük, azt a felmondáskor egyeztetjük.</p>"),
("<p>A szervered és az adataid a tiéd. Az összes alkalmazás nyílt forráskódú, ami azt jelenti, hogy bárki más is üzemeltetheti — nem függsz egyetlen szolgáltatótól sem.",
"<p>A szervered és az adataid a tiéd. Az alkalmazások szinte mind nyílt forráskódúak, ami azt jelenti, hogy bárki más is üzemeltetheti őket — nem függsz egyetlen szolgáltatótól sem."),
("Minden szoftver, amit telepítünk, <strong>100% ingyenes és nyílt forráskódú</strong> — nincs licencdíj, nincs előfizetés az alkalmazásokért.</p>",
"Az alkalmazások ingyenesen használhatók, és szinte mind <strong>nyílt forráskódú</strong> — nincs licencdíj az alkalmazásokért. (Néhánynak van fizetős extrája, például a Plex Pass; ez választható.)</p>"),
("<p>Ha offsite mentéshez titkosított felhő tárhelyet választasz, annak van havi költsége — de ez általában havi néhány száz forint, és vannak ingyenes alternatívák is (pl. „haver rendszer\").</p>",
"<p>A távoli mentés tárhelyének van havi költsége, ami a mentett adatmennyiségtől függ — ezt az ajánlatban előre leírjuk.</p>"),
("Az alkalmazásaid Docker konténerekben futnak egy elkülönített rendszerkörnyezetben, a Proxmox alapra telepítve. Így minden változtatás előtt pillanatképet készíthetünk, és az egész rendszered biztonságosan visszaállítható akár egy új gépre is.</p>",
"Az alkalmazásaid Docker konténerekben futnak egy elkülönített rendszerkörnyezetben, a Proxmox alapra telepítve. Így a teljes rendszerkörnyezet is menthető, és ebből visszaállítható.</p>"),
("<strong>Pillanatkép minden változtatás előtt</strong> — frissítés vagy beállítás előtt a rendszer „fényképet\" készít önmagáról, így bármi gond esetén pillanatok alatt visszaállunk az előző állapotra.</p>",
"<strong>Mentés frissítés előtt</strong> — egy alkalmazás frissítése előtt friss mentés készül, és ha az új verzió nem indul el, a doboz magától visszaállítja az előzőt.</p>"),
("<strong>Az egész rendszer menthető és visszaállítható</strong> — nem csak az adataid, hanem a teljes dobozod is mentésbe tehető, és szükség esetén egy új gépre költöztethető.</p>",
"<strong>Az egész rendszer menthető és visszaállítható</strong> — nem csak az adataid, hanem a teljes dobozod is mentésbe kerül.</p>"),
("<p>Igen, és ezt kifejezetten ajánljuk! A VPN (Virtual Private Network) titkosított alagutat hoz létre az eszközöd és az otthoni szervered között. Ez a legbiztonságosabb módja a távoli elérésnek — mintha a kanapédon ülnél, miközben egy kávézóból dolgozol.</p>\n <p>Modern VPN megoldásokat használunk, amelyek egyszerű mobil applikációval működnek — nem kell hozzá semmilyen technikai tudás.</p>",
"<p>Nem kell hozzá: a doboz alapból egy titkosított alagúton át, HTTPS-sel érhető el bárhonnan, külön alkalmazás nélkül.</p>\n <p>Háztartási VPN-elérést (amikor a telefonod vagy a laptopod úgy csatlakozik, mintha otthon lenne) ma nem állítunk be a dobozon. Ha erre van szükséged, kérdezz rá.</p>"),
]
R += [
("Mi ezzel szemben egy nyílt, Docker-alapú rendszert kínálunk, amelyre <strong>bármilyen</strong> nyílt forráskódú alkalmazás telepíthető — fotókezelőtől a média szerveren át a jelszókezelőig.</p>",
"Mi ezzel szemben egy nyílt, Docker-alapú rendszert kínálunk, amelyre a katalógus alkalmazásai pár kattintással telepíthetők — fotókezelőtől a média szerveren át a jelszókezelőig.</p>"),
("<p>Az adatok nem vesznek el áramkimaradás esetén — az adatbázisok és fájlrendszerek felkészültek erre. Ha a megbízhatóság kritikus,",
"<p>Áramszünet után a doboz magától helyreáll — ezt valódi áramszünettel is kipróbáltuk. Ha a megbízhatóság kritikus,"),
]
for old, new in R:
s = sub1(s, old, new)
def rebuild_faq_jsonld(page):
"""FAQPage JSON-LD = every visible question with its visible answer (text only, paragraphs joined)."""
items = re.findall(r'<button class="faq-question">\s*(.*?)\s*<svg class="faq-chevron".*?<div class="faq-answer-inner">(.*?)</div></div>', page, re.S)
ents = []
for q, a in items:
parts = re.findall(r"<(?:p|li)[^>]*>(.*?)</(?:p|li)>", a, re.S)
text = " ".join(re.sub(r"\s+", " ", html.unescape(re.sub(r"<[^>]+>", "", p))).strip() for p in parts)
ents.append({"@type": "Question", "name": html.unescape(q.strip()),
"acceptedAnswer": {"@type": "Answer", "text": text}})
body = json.dumps({"@context": "https://schema.org", "@type": "FAQPage", "mainEntity": ents},
ensure_ascii=False, indent=2)
body = "\n".join(" " + l for l in body.split("\n"))
new, n = re.subn(r'<script type="application/ld\+json">.*?</script>',
lambda m: '<script type="application/ld+json">\n' + body + "\n </script>", page, count=1, flags=re.S)
assert n == 1
return new, len(ents)
s, nq = rebuild_faq_jsonld(s)
print("gyik: JSON-LD rebuilt from %d visible questions" % nq)
s = set_alternates(s, "/gyik", "/en/faq")
s = set_nav(s, "hu", "/gyik", "/en/faq")
write("gyik.html", s)
# ------------------------------------------------------------------ kapcsolat.html (no claim changed; nav + hreflang)
s = head_text("kapcsolat.html")
s = set_alternates(s, "/kapcsolat", "/en/contact")
s = set_nav(s, "hu", "/kapcsolat", "/en/contact")
write("kapcsolat.html", s)
# ------------------------------------------------------------------ letoltes.html: the R-559 header link gives way to the nav switch
s = head_text("letoltes.html")
s = sub1(s, '\n <p><a href="/en/download" lang="en" hreflang="en">English</a></p>', "")
s = set_alternates(s, "/letoltes", "/en/download")
s = set_nav(s, "hu", None, "/en/download")
write("letoltes.html", s)
@@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
"""szolgaltatasok-nonpublic.html: no twin and no claim change (it carries no app count); only the shared nav gains
the language switch, pointing at the English home."""
import sys
sys.path.insert(0, __file__.rsplit("/", 1)[0])
from common import *
s = head_text("szolgaltatasok-nonpublic.html")
s = set_nav(s, "hu", None, "/en/")
write("szolgaltatasok-nonpublic.html", s)
print("written szolgaltatasok-nonpublic.html")
@@ -0,0 +1,46 @@
"""og-image-en.png: the English twin of og-image.png (1200x630). The Hungarian text areas are repainted from the card's
own radial background profile, then the English text is drawn in the site's own font (Plus Jakarta Sans, vendored)."""
import math, sys
from PIL import Image, ImageDraw, ImageFont, ImageFilter
A = "/mnt/5_hdd/felhom.eu/git/felhom.eu/website/assets/"
im = Image.open(A + "og-image.png").convert("RGB")
W_, H_ = im.size
px = im.load()
boxes = [(60, 190, 760, 370), (60, 380, 1060, 470), (60, 505, 460, 580)] # headline, subtitle, button + url
def inbox(x, y): return any(a <= x <= c and b <= y <= d for a, b, c, d in boxes)
blur = im.filter(ImageFilter.GaussianBlur(25))
cx, cy, best = 0, 0, -1
for y in range(100, 400, 4):
for x in range(700, 1199, 4):
r, g, b = blur.getpixel((x, y))
if b > best: best, cx, cy = b, x, y
prof = {}
for y in range(8, H_):
for x in range(0, W_, 2):
if inbox(x, y) or (x < 450 and y < 160): continue
r = int(math.hypot(x - cx, y - cy))
prof.setdefault(r, []).append(px[x, y])
med = {}
for r, v in prof.items():
v.sort(key=lambda t: t[2]); med[r] = v[len(v) // 2]
def at(r):
while r not in med and r > 0: r -= 1
return med.get(r, (10, 18, 32))
for a, b, c, d in boxes:
for y in range(b, d + 1):
for x in range(a, c + 1):
px[x, y] = at(int(math.hypot(x - cx, y - cy)))
dr = ImageDraw.Draw(im)
def font(size, wght):
f = ImageFont.truetype(A + "fonts/pjs-latin.woff2", size); f.set_variation_by_axes([wght]); return f
h = font(78, 800); sub = font(27, 400); btn = font(23, 700); url = font(23, 700)
dr.text((72, 196), "Your own cloud,", font=h, fill=(237, 242, 249))
dr.text((72, 280), "your own rules", font=h, fill=(46, 168, 245))
dr.text((72, 388), "A home server in your own home in Hungary: photos, documents,", font=sub, fill=(148, 166, 191))
dr.text((72, 426), "media — with automatic backups and a recovery code only you know.", font=sub, fill=(148, 166, 191))
tw = dr.textlength("Closed test starting", font=btn)
dr.rectangle((72, 517, 72 + tw + 44, 566), fill=(46, 168, 245))
dr.text((94, 528), "Closed test starting", font=btn, fill=(10, 18, 32))
dr.text((72 + tw + 44 + 22, 528), "felhom.eu", font=url, fill=(94, 115, 146))
im.save(A + "og-image-en.png", optimize=True)
print("glow centre", cx, cy, "written og-image-en.png")