diff --git a/apps/website/Dockerfile b/apps/website/Dockerfile index 4702440..b472592 100644 --- a/apps/website/Dockerfile +++ b/apps/website/Dockerfile @@ -62,6 +62,7 @@ RUN set -eu; \ COPY httpd-php-fpm.conf /etc/apache2/conf.d/php-fpm.conf COPY opcache.ini /usr/local/etc/php/conf.d/zz-opcache.ini COPY php-fpm-runtime.conf /usr/local/etc/php-fpm.d/zz-runtime.conf +COPY render-static-pages.sh /usr/local/bin/render-static-pages COPY start-website.sh /usr/local/bin/start-website COPY . /var/www/localhost/htdocs/ @@ -74,10 +75,13 @@ RUN rm -f /var/www/localhost/htdocs/index.html && \ /var/log/apache2 \ /var/www/localhost/htdocs/db \ /tmp/website-lang && \ + chmod 0755 /usr/local/bin/render-static-pages && \ + /usr/local/bin/render-static-pages /var/www/localhost/htdocs && \ chown -R apache:apache \ /run/apache2 \ /var/log/apache2 \ /var/www/localhost/htdocs/db \ + /var/www/localhost/htdocs/static-pages \ /tmp/website-lang && \ chmod 0755 /usr/local/bin/start-website diff --git a/apps/website/blog.php b/apps/website/blog.php index b7d67f0..41f58be 100644 --- a/apps/website/blog.php +++ b/apps/website/blog.php @@ -1,6 +1,5 @@ [ @@ -814,7 +811,12 @@ function renderStackSourceLinks(string $stackKey, array $sourceLinks, string $so -
+

- -

- -

- -
@@ -893,6 +868,7 @@ function renderStackSourceLinks(string $stackKey, array $sourceLinks, string $so window.OTHER_PAGES = ['/index.php', '/cv.php', '/homelab-tree.php']; + diff --git a/apps/website/httpd-php-fpm.conf b/apps/website/httpd-php-fpm.conf index dc468b8..750f67f 100644 --- a/apps/website/httpd-php-fpm.conf +++ b/apps/website/httpd-php-fpm.conf @@ -1,3 +1,27 @@ +RewriteEngine On + +RewriteCond %{QUERY_STRING} (^|&)lang=en(&|$) +RewriteRule ^/?(?:index\.php)?$ /static-pages/en/index.html [END] +RewriteCond %{QUERY_STRING} (^|&)lang=en(&|$) +RewriteRule ^/?(cv|blog|demos|homelab-tree)\.php$ /static-pages/en/$1.html [END] + +RewriteCond %{QUERY_STRING} (^|&)lang=nah(&|$) +RewriteRule ^/?(?:index\.php)?$ /static-pages/nah/index.html [END] +RewriteCond %{QUERY_STRING} (^|&)lang=nah(&|$) +RewriteRule ^/?(cv|blog|demos|homelab-tree)\.php$ /static-pages/nah/$1.html [END] + +RewriteCond %{QUERY_STRING} !(^|&)lang= [NC] +RewriteCond %{HTTP:Accept-Language} ^en [NC] +RewriteRule ^/?(?:index\.php)?$ /static-pages/en/index.html [END] +RewriteCond %{QUERY_STRING} !(^|&)lang= [NC] +RewriteCond %{HTTP:Accept-Language} ^en [NC] +RewriteRule ^/?(cv|blog|demos|homelab-tree)\.php$ /static-pages/en/$1.html [END] + +RewriteCond %{QUERY_STRING} !(^|&)lang= [NC] +RewriteRule ^/?(?:index\.php)?$ /static-pages/nah/index.html [END] +RewriteCond %{QUERY_STRING} !(^|&)lang= [NC] +RewriteRule ^/?(cv|blog|demos|homelab-tree)\.php$ /static-pages/nah/$1.html [END] + SetHandler "proxy:fcgi://127.0.0.1:9000" diff --git a/apps/website/render-static-pages.sh b/apps/website/render-static-pages.sh new file mode 100644 index 0000000..5ecd733 --- /dev/null +++ b/apps/website/render-static-pages.sh @@ -0,0 +1,32 @@ +#!/bin/sh +set -eu + +root="${1:-/var/www/localhost/htdocs}" + +render_page() { + lang="$1" + source="$2" + output="$3" + request_uri="$4" + + mkdir -p "$(dirname "$output")" + # shellcheck disable=SC2016 + php -r ' + parse_str($argv[1], $_GET); + $_SERVER["HTTP_ACCEPT_LANGUAGE"] = $argv[2]; + $_SERVER["REQUEST_METHOD"] = "GET"; + $_SERVER["REQUEST_URI"] = $argv[3]; + $_SERVER["PHP_SELF"] = "/" . $argv[4]; + chdir($argv[5]); + include $argv[5] . "/" . $argv[4]; + ' "lang=$lang" "$lang" "$request_uri" "$source" "$root" > "$output" +} + +for lang in en nah; do + static_dir="$root/static-pages/$lang" + render_page "$lang" index.php "$static_dir/index.html" "/?lang=$lang" + render_page "$lang" cv.php "$static_dir/cv.html" "/cv.php?lang=$lang" + render_page "$lang" blog.php "$static_dir/blog.html" "/blog.php?lang=$lang" + render_page "$lang" demos.php "$static_dir/demos.html" "/demos.php?lang=$lang" + render_page "$lang" homelab-tree.php "$static_dir/homelab-tree.html" "/homelab-tree.php?lang=$lang" +done diff --git a/apps/website/visitor-ideas.js b/apps/website/visitor-ideas.js new file mode 100644 index 0000000..966b888 --- /dev/null +++ b/apps/website/visitor-ideas.js @@ -0,0 +1,68 @@ +(function () { + const root = document.getElementById('visitor-ideas'); + if (!root) return; + + const messages = { + thanks: root.dataset.statusThanks, + invalid: root.dataset.statusInvalid, + slow: root.dataset.statusSlow, + error: root.dataset.statusError, + }; + + const status = new URLSearchParams(window.location.search).get('idea'); + if (status && messages[status]) { + const statusEl = document.createElement('p'); + statusEl.className = `idea-status idea-status-${status}`; + statusEl.textContent = messages[status]; + const form = root.querySelector('.idea-form'); + root.insertBefore(statusEl, form || root.firstChild); + } + + function addIdeaText(parent, text) { + String(text || '').split('\n').forEach((line, index) => { + if (index > 0) parent.appendChild(document.createElement('br')); + parent.appendChild(document.createTextNode(line)); + }); + } + + function renderIdeas(ideas) { + const existing = root.querySelector('.visitor-idea-list'); + if (existing) existing.remove(); + if (!Array.isArray(ideas) || ideas.length === 0) return; + + const list = document.createElement('div'); + list.className = 'visitor-idea-list'; + + const heading = document.createElement('h4'); + heading.textContent = root.dataset.recentTitle || 'Recent visitor ideas'; + list.appendChild(heading); + + ideas.forEach((idea) => { + const article = document.createElement('article'); + article.className = 'visitor-idea-card'; + + const body = document.createElement('p'); + addIdeaText(body, idea.idea); + article.appendChild(body); + + const footer = document.createElement('footer'); + const name = document.createElement('span'); + name.textContent = idea.name || 'Anonymous visitor'; + const time = document.createElement('time'); + time.dateTime = idea.created_at || ''; + time.textContent = String(idea.created_at || '').slice(0, 10); + footer.appendChild(name); + footer.appendChild(time); + article.appendChild(footer); + + list.appendChild(article); + }); + + root.appendChild(list); + } + + fetch('/visitor_ideas.php', { headers: { Accept: 'application/json' } }) + .then((response) => (response.ok ? response.json() : { ideas: [] })) + .then((data) => renderIdeas(data.ideas)) + .catch(() => renderIdeas([])); +})(); diff --git a/apps/website/visitor_ideas.php b/apps/website/visitor_ideas.php new file mode 100644 index 0000000..fe50291 --- /dev/null +++ b/apps/website/visitor_ideas.php @@ -0,0 +1,14 @@ + visitor_ideas_read()], + JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES +);