website: send Hub early access form submission to posthog

This commit is contained in:
Pasha Sviderski
2026-07-09 18:56:46 +10:00
parent e684795aee
commit f8e6d1caaa
5 changed files with 137 additions and 12 deletions
+14
View File
@@ -3,6 +3,20 @@
}
:8000 {
# Reverse proxy PostHog through our own domain so ad blockers don't drop analytics requests.
# Static assets (e.g. array.js) are served from a separate PostHog assets host.
handle_path /phproxy/static/* {
rewrite * /static{uri}
reverse_proxy https://us-assets.i.posthog.com {
header_up Host us-assets.i.posthog.com
}
}
handle_path /phproxy/* {
reverse_proxy https://us.i.posthog.com {
header_up Host us.i.posthog.com
}
}
root * /usr/share/caddy
file_server
log
+75 -6
View File
@@ -32,6 +32,8 @@
content="Uncloud Hub will add a dashboard, metrics, logs, and alerts to the Uncloud clusters you already run. Skip the weekend of wiring Prometheus, Loki, and Grafana. Join the early access list.">
<meta name="twitter:image" content="https://uncloud.run/images/logo-wide.png">
<meta name="twitter:image:alt" content="Uncloud Hub - Observability for self-hosted apps">
<script src="./js/posthog.js" defer></script>
</head>
<body class="font-inter antialiased bg-white text-zinc-900 tracking-tight">
@@ -205,8 +207,7 @@
class="group block relative text-center md:px-5 after:hidden md:after:block after:absolute after:right-0 after:top-1/2 after:-translate-y-1/2 after:w-px after:h-8 after:border-l after:border-zinc-300 after:border-dashed last:after:hidden">
<h4 class="font-inter-tight text-2xl md:text-3xl font-bold tabular-nums mb-2"><span
x-data="counter(390)" x-text="counterValue">390</span>+</h4>
<p class="text-zinc-500 transition-colors group-hover:text-zinc-800">Discord
members</p>
<p class="text-zinc-500 transition-colors group-hover:text-zinc-800">Fans on Discord</p>
</a>
</div>
@@ -819,7 +820,7 @@
</p>
</div>
<form action="/fill-form" method="POST">
<form id="hub-early-access-form">
<div class="mb-4">
<label for="hub-email" class="block text-sm font-medium text-zinc-300 mb-1.5">Your
email</label>
@@ -836,12 +837,39 @@
placeholder="e.g. searchable logs across all my services, or alerts when something goes down..."
class="form-textarea w-full bg-white border-zinc-700 rounded-lg px-4 py-3 text-sm text-zinc-900 resize-none"></textarea>
</div>
<button type="submit"
class="btn text-zinc-900 bg-white hover:bg-zinc-100 w-full shadow font-semibold">
<!-- Disabled until PostHog which processes the submissions is loaded. -->
<button type="submit" disabled
class="btn text-zinc-900 bg-white hover:bg-zinc-100 w-full shadow font-semibold disabled:opacity-70">
Request early access
</button>
</form>
<!-- Error shown below the form when submissions can't be sent, e.g. PostHog can't be loaded. -->
<p id="hub-form-error" class="hidden text-center text-sm text-red-400 mt-4">
Sorry, the form isn't working. It may be blocked by a browser extension.<br>
Please email
<a href="mailto:pasha@uncloud.run"
class="underline hover:text-red-300 transition">pasha@uncloud.run</a>
or message @psviderski on
<a href="https://uncloud.run/discord" target="_blank" rel="noopener"
class="underline hover:text-red-300 transition">Discord</a>
instead.
</p>
<!-- Success message shown in place of the form after submission. -->
<div id="hub-form-success" class="hidden text-center bg-zinc-800 rounded-lg px-6 py-8">
<p class="text-lg font-semibold text-white">You're on the list!</p>
<p class="text-zinc-400 mt-2">
We'll email you an invite as Hub becomes usable. Thanks for helping shape it.
</p>
<p class="text-zinc-400 mt-2">
Meanwhile,
<a href="https://uncloud.run/discord" target="_blank" rel="noopener"
class="underline text-zinc-300 hover:text-white transition">join our Discord</a>
to follow the development and chat about what you'd like Hub to do.
</p>
</div>
<p class="text-center text-sm text-zinc-500 mt-6">
Uncloud updates only, no spam. Or email
<a href="mailto:pasha@uncloud.run"
@@ -1085,7 +1113,48 @@
this.observer && this.observer.disconnect()
},
}))
})
});
// Send early access form submissions to PostHog and show an inline success message.
const form = document.getElementById('hub-early-access-form');
const formSuccess = document.getElementById('hub-form-success');
const formError = document.getElementById('hub-form-error');
const submitButton = form.querySelector('button[type="submit"]');
// The submit button starts disabled and is enabled once PostHog is ready to accept submissions.
// If it doesn't load in time, show an error but keep polling in case it loads late.
const posthogDeadline = Date.now() + 5000;
(function enableFormWhenPostHogLoads() {
if (window.posthog && window.posthog.__loaded) {
submitButton.disabled = false;
formError.classList.add('hidden');
return;
}
if (Date.now() > posthogDeadline) {
formError.classList.remove('hidden');
}
setTimeout(enableFormWhenPostHogLoads, 200);
})();
form.addEventListener('submit', function (e) {
e.preventDefault();
const email = form.elements.email.value.trim();
if (!email) return;
try {
// Key the person by email so the early access list shows up under People in PostHog.
posthog.identify(email, {email: email});
posthog.capture('hub_early_access_requested', {
email: email,
message: form.elements.message.value.trim(),
});
} catch (err) {
console.error('Failed to send the form:', err);
formError.classList.remove('hidden');
return;
}
form.classList.add('hidden');
formSuccess.classList.remove('hidden');
});
})();
</script>
+1 -2
View File
@@ -32,9 +32,8 @@
content="Take your Docker Compose apps to production with zero-downtime deployments, automatic HTTPS, and cross-machine scaling. Self-hosting made reliable without the complexity.">
<meta name="twitter:image" content="https://uncloud.run/images/logo-wide.png">
<meta name="twitter:image:alt" content="Uncloud logo - Self-host web apps with ease">
<style>
</style>
<script src="./js/posthog.js" defer></script>
</head>
<body class="font-inter antialiased bg-white text-zinc-900 tracking-tight">
+15
View File
@@ -0,0 +1,15 @@
// PostHog analytics and form processing for the landing pages.
// The project API key is public and safe to expose in client-side code.
const POSTHOG_API_KEY = 'phc_nsuhPtAsiYAFiSYmc2KwA5Homz6miXWjf3Hy4J4H3QMV';
(function () {
// Official PostHog JS snippet from Project settings - General - HTML snippet.
!function(t,e){var o,n,p,r;e.__SV||(window.posthog && window.posthog.__loaded)||(window.posthog=e,e._i=[],e.init=function(i,s,a){function g(t,e){var o=e.split(".");2==o.length&&(t=t[o[0]],e=o[1]),t[e]=function(){t.push([e].concat(Array.prototype.slice.call(arguments,0)))}}(p=t.createElement("script")).type="text/javascript",p.crossOrigin="anonymous",p.async=!0,p.src=s.api_host.replace(".i.posthog.com","-assets.i.posthog.com")+"/static/array.js",(r=t.getElementsByTagName("script")[0]).parentNode.insertBefore(p,r);var u=e;for(void 0!==a?u=e[a]=[]:a="posthog",u.people=u.people||[],u.toString=function(t){var e="posthog";return"posthog"!==a&&(e+="."+a),t||(e+=" (stub)"),e},u.people.toString=function(){return u.toString(1)+".people (stub)"},o="ki Ci init qi Hi pr Bi zi Di capture calculateEventProperties Qi register register_once register_for_session unregister unregister_for_session Ki getFeatureFlag getFeatureFlagPayload getFeatureFlagResult getAllFeatureFlags isFeatureEnabled reloadFeatureFlags updateFlags updateEarlyAccessFeatureEnrollment getEarlyAccessFeatures on onFeatureFlags onSurveysLoaded onSessionId getSurveys getActiveMatchingSurveys renderSurvey displaySurvey cancelPendingSurvey canRenderSurvey canRenderSurveyAsync Xi identify setPersonProperties unsetPersonProperties group resetGroups setPersonPropertiesForFlags resetPersonPropertiesForFlags setGroupPropertiesForFlags resetGroupPropertiesForFlags reset shutdown setIdentity clearIdentity get_distinct_id getGroups get_session_id get_session_replay_url alias set_config startSessionRecording stopSessionRecording sessionRecordingStarted captureException addExceptionStep captureLog startExceptionAutocapture stopExceptionAutocapture loadToolbar get_property getSessionProperty Ji Gi createPersonProfile setInternalOrTestUser Yi Ai rn opt_in_capturing opt_out_capturing has_opted_in_capturing has_opted_out_capturing get_explicit_consent_status is_capturing clear_opt_in_out_capturing Vi debug mr it getPageViewId captureTraceFeedback captureTraceMetric Oi".split(" "),n=0;n<o.length;n++)g(u,o[n]);e._i.push([i,s,a])},e.__SV=1)}(document,window.posthog||[]);
posthog.init(POSTHOG_API_KEY, {
// Send events through our own domain (see website/Caddyfile) so ad blockers don't drop them.
api_host: window.location.origin + '/phproxy',
ui_host: "https://us.posthog.com",
defaults: '2026-05-30',
person_profiles: 'identified_only',
})
})();
+32 -4
View File
@@ -1215,10 +1215,6 @@ input[type="search"]::-webkit-search-results-decoration {
top: 0.5rem;
}
.isolate{
isolation: isolate;
}
.z-10{
z-index: 10;
}
@@ -2515,6 +2511,29 @@ html {
line-height: 1.2;
}
/* A hand-swiped stroke with uneven rounded edges. */
.marker-red {
position: relative;
white-space: nowrap;
color: #fff;
isolation: isolate;
}
.marker-red::before {
content: "";
position: absolute;
z-index: -1;
inset: 0.08em -0.18em -0.02em -0.14em;
background: linear-gradient(100deg,
rgba(220, 38, 38, 0.82) 0%,
rgba(239, 68, 68, 0.95) 28%,
rgba(225, 29, 72, 0.9) 72%,
rgba(220, 38, 38, 0.88) 100%);
border-radius: 0.3em 0.5em 0.4em 0.6em;
transform: skew(-10deg) rotate(-0.5deg);
}
.before\:pointer-events-none::before{
content: var(--tw-content);
pointer-events: none;
@@ -2692,6 +2711,11 @@ html {
background-color: rgb(39 39 42 / var(--tw-bg-opacity));
}
.hover\:text-red-300:hover{
--tw-text-opacity: 1;
color: rgb(252 165 165 / var(--tw-text-opacity));
}
.hover\:text-violet-900:hover{
--tw-text-opacity: 1;
color: rgb(76 29 149 / var(--tw-text-opacity));
@@ -2803,6 +2827,10 @@ html {
--tw-ring-offset-width: 2px;
}
.disabled\:opacity-70:disabled{
opacity: 0.7;
}
.group[open] .group-open\:rotate-45{
--tw-rotate: 45deg;
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));