diff --git a/public/astronaut.png b/public/astronaut.png new file mode 100644 index 0000000..bd441c6 Binary files /dev/null and b/public/astronaut.png differ diff --git a/src/middleware.ts b/src/middleware.ts new file mode 100644 index 0000000..bff0e28 --- /dev/null +++ b/src/middleware.ts @@ -0,0 +1,54 @@ +import { defineMiddleware } from 'astro:middleware' + +// Pre-launch gate: clients unlock the real site via a secret link +// (https://your-domain/?preview=PREVIEW_SECRET); everyone else — strangers and +// bots — is shown src/pages/coming-soon.astro. The gate is active only while +// SHOW_COMING_SOON=true; unset it (or set anything else) to take the site live. + +const COOKIE_NAME = 'preview_access' +const COOKIE_MAX_AGE = 60 * 60 * 24 * 7 // 7 days +const COMING_SOON_ROUTE = '/coming-soon' + +export const onRequest = defineMiddleware((context, next) => { + // Prefer process.env so values can be set at deploy/runtime (Docker); fall + // back to import.meta.env so local `astro dev` picks them up from .env (Vite + // only loads .env into import.meta.env, not process.env). + const secret = process.env.PREVIEW_SECRET ?? import.meta.env.PREVIEW_SECRET + const showComingSoon = + (process.env.SHOW_COMING_SOON ?? import.meta.env.SHOW_COMING_SOON) === 'true' + + // Gate disabled — site is live. + if (!showComingSoon) { + return next() + } + + const { cookies, url, redirect } = context + + // Magic preview link: ?preview=SECRET remembers the client in a cookie, then + // we redirect to the same URL without the token so it isn't visible/forwarded. + const previewParam = url.searchParams.get('preview') + if (previewParam !== null) { + if (secret && previewParam === secret) { + cookies.set(COOKIE_NAME, secret, { + httpOnly: true, + sameSite: 'lax', + secure: url.protocol === 'https:', + path: '/', + maxAge: COOKIE_MAX_AGE, + }) + } + url.searchParams.delete('preview') + return redirect(url.pathname + url.search) + } + + // Unlocked clients, the launch page, and robots.txt get the real render + // (robots.txt must reach bots so it can tell them to stay out while gated). + const isClient = Boolean(secret) && cookies.get(COOKIE_NAME)?.value === secret + if (isClient || url.pathname === COMING_SOON_ROUTE || url.pathname === '/robots.txt') { + return next() + } + + // Everyone else is shown the launch page while keeping the original URL. + // next(path) rewrites internally without re-running this middleware. + return next(COMING_SOON_ROUTE) +}) diff --git a/src/pages/coming-soon.astro b/src/pages/coming-soon.astro new file mode 100644 index 0000000..4192fb0 --- /dev/null +++ b/src/pages/coming-soon.astro @@ -0,0 +1,96 @@ +--- +// Standalone "Getting ready for launch" page shown to everyone who is NOT an +// unlocked client (strangers, search engines, bots). The gate in +// src/middleware.ts internally rewrites unauthorized requests to this route. +// +// It is intentionally self-contained — no shared Layout and no Strapi data — so +// it renders even while the rest of the site is gated. Edit the copy in
. +Astro.response.headers.set('X-Robots-Tag', 'noindex, nofollow') +--- + + + + + + + + Скоро запуск + + +
+ +

Мы готовимся к запуску

+

Сайт скоро станет доступен. Спасибо, что заглянули — мы уже совсем близко.

+
+ + + + diff --git a/src/pages/robots.txt.ts b/src/pages/robots.txt.ts new file mode 100644 index 0000000..9a53e77 --- /dev/null +++ b/src/pages/robots.txt.ts @@ -0,0 +1,19 @@ +import type { APIRoute } from 'astro' + +// robots.txt driven by the same SHOW_COMING_SOON flag as the pre-launch gate +// (see src/middleware.ts). While gated, keep the whole site out of search +// indexes; once live, allow crawling. Read at request time so it can be flipped +// via the deploy/runtime environment without a rebuild. +export const prerender = false + +export const GET: APIRoute = () => { + const gated = + (process.env.SHOW_COMING_SOON ?? import.meta.env.SHOW_COMING_SOON) === 'true' + const body = gated + ? 'User-agent: *\nDisallow: /\n' + : 'User-agent: *\nDisallow:\n' + + return new Response(body, { + headers: { 'Content-Type': 'text/plain; charset=utf-8' }, + }) +}