Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 58 additions & 1 deletion apps/web/astro.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,62 @@ export default defineConfig({
site: "https://ephemask.com",
output: "server",
adapter: vercel(),
integrations: [svelte(), react(), tailwind(), sitemap()],
i18n: {
defaultLocale: "en",
locales: [
"en",
{ path: "pt-br", codes: ["pt-BR", "pt"] },
"es",
"fr",
],
routing: {
prefixDefaultLocale: false,
fallbackType: "rewrite",
},
fallback: {
"pt-br": "en",
es: "en",
fr: "en",
},
},
integrations: [
svelte(),
react(),
tailwind(),
sitemap({
i18n: {
defaultLocale: "en",
locales: {
en: "en",
"pt-br": "pt-BR",
es: "es",
fr: "fr",
},
},
filter: (page) =>
!page.includes("/admin") &&
!page.includes("/auth/") &&
!page.includes("/inbox"),
customPages: [
"https://ephemask.com/pt-br/",
"https://ephemask.com/pt-br/account/",
"https://ephemask.com/pt-br/login/",
"https://ephemask.com/pt-br/docs/",
"https://ephemask.com/pt-br/privacy/",
"https://ephemask.com/pt-br/terms/",
"https://ephemask.com/es/",
"https://ephemask.com/es/account/",
"https://ephemask.com/es/login/",
"https://ephemask.com/es/docs/",
"https://ephemask.com/es/privacy/",
"https://ephemask.com/es/terms/",
"https://ephemask.com/fr/",
"https://ephemask.com/fr/account/",
"https://ephemask.com/fr/login/",
"https://ephemask.com/fr/docs/",
"https://ephemask.com/fr/privacy/",
"https://ephemask.com/fr/terms/",
],
}),
],
});
6 changes: 6 additions & 0 deletions apps/web/src/app.d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
/// <reference types="astro/client" />

declare namespace App {
interface Locals {
locale: import("@ephemask/shared").Locale;
}
}

declare namespace svelteHTML {
interface HTMLAttributes {
onclick?: (event: MouseEvent) => void;
Expand Down
51 changes: 36 additions & 15 deletions apps/web/src/components/AccountView.svelte
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<script lang="ts">
import { onMount } from "svelte";
import { t } from "@ephemask/shared";
import { initLocale, useLocale } from "../lib/i18n.svelte";
import { initLocale, useLocale, localizePath } from "../lib/i18n.svelte";
import { createApiClient, type Locale } from "@ephemask/shared";
import { useUser, initUser, registerUser, refreshUser, logout } from "../lib/user.svelte";
import { initPurchases, purchasePremium, openManagementPortal } from "../lib/purchases";
Expand Down Expand Up @@ -102,19 +102,17 @@
}
}

onMount(async () => {
initLocale();
await initUser();
loadInboxes();

function onVisibilityChange() {
if (document.visibilityState === "visible") {
refreshUser();
loadInboxes();
}
function onVisibilityChange() {
if (document.visibilityState === "visible") {
refreshUser();
loadInboxes();
}
document.addEventListener("visibilitychange", onVisibilityChange);
}

onMount(() => {
initLocale();
initUser().then(() => loadInboxes());
document.addEventListener("visibilitychange", onVisibilityChange);
return () => document.removeEventListener("visibilitychange", onVisibilityChange);
});
</script>
Expand All @@ -123,13 +121,13 @@
<div class="min-h-screen flex flex-col">
<!-- Header -->
<header class="px-6 py-5 flex items-center justify-between border-b border-ghost/15">
<a href="/" class="flex items-center gap-2 hover:opacity-80 transition-opacity">
<a href={localizePath("/")} class="flex items-center gap-2 hover:opacity-80 transition-opacity">
<div class="w-2 h-2 rounded-full bg-neon pulse-ring"></div>
<span class="font-display text-xs tracking-widest text-muted uppercase">ephemask</span>
</a>
<div class="flex items-center gap-4">
<LocaleToggle />
<a href="/" class="text-xs text-muted font-mono hover:text-neon transition-colors">{t("ctaGenerate")}</a>
<a href={localizePath("/")} class="text-xs text-muted font-mono hover:text-neon transition-colors">{t("ctaGenerate")}</a>
</div>
</header>

Expand Down Expand Up @@ -318,7 +316,7 @@
{apiKeyCopied ? "✓" : t("copy")}
</button>
</div>
<a href="/docs" class="inline-block mt-3 text-[10px] font-mono text-ghost hover:text-neon transition-colors">
<a href={localizePath("/docs")} class="inline-block mt-3 text-[10px] font-mono text-ghost hover:text-neon transition-colors">
API Documentation →
</a>
</div>
Expand Down Expand Up @@ -347,6 +345,29 @@
<VanityInboxForm />
<DomainManager />
{/if}

<!-- Danger Zone -->
<div class="bg-ember/5 border border-ember/20 rounded-sm p-5 mt-8">
<h3 class="text-ember text-sm font-display tracking-wider mb-3">{t("dangerZone")}</h3>
{#if user.isPremium}
<p class="text-muted text-xs font-mono mb-3">{t("cancelSubscriptionFirst")}</p>
{/if}
<button
onclick={async () => {
if (user.isPremium) return;
if (!confirm(t("deleteAccountConfirm"))) return;
if (!user.apiKey) return;
await api.deleteAccount(user.apiKey);
logout();
window.location.href = localizePath("/");
}}
disabled={user.isPremium}
class="px-4 py-2 text-xs font-mono tracking-wider border border-ember/40 text-ember
hover:bg-ember/10 transition-all duration-300 disabled:opacity-40 disabled:cursor-not-allowed"
>
{t("deleteAccount")}
</button>
</div>
</div>
{/if}
</main>
Expand Down
8 changes: 4 additions & 4 deletions apps/web/src/components/AdminView.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
type MessageDetail as MessageDetailType,
getLocale,
} from "@ephemask/shared";
import { initLocale } from "../lib/i18n.svelte";
import { initLocale, localizePath } from "../lib/i18n.svelte";
import { useUser, initUser } from "../lib/user.svelte";

const api = createApiClient(import.meta.env.PUBLIC_API_URL || "");
Expand Down Expand Up @@ -139,7 +139,7 @@
await initUser();

if (!user.isAdmin) {
window.location.href = "/";
window.location.href = localizePath("/");
return;
}

Expand All @@ -156,13 +156,13 @@
<!-- Header -->
<header class="px-6 py-4 flex items-center justify-between border-b border-ghost/15">
<div class="flex items-center gap-3">
<a href="/" class="flex items-center gap-2 hover:opacity-80 transition-opacity">
<a href={localizePath("/")} class="flex items-center gap-2 hover:opacity-80 transition-opacity">
<div class="w-2 h-2 rounded-full bg-neon pulse-ring"></div>
<span class="font-display text-xs tracking-widest text-muted uppercase">ephemask</span>
</a>
<span class="text-ember text-[10px] font-mono tracking-widest">ADMIN</span>
</div>
<a href="/account" class="text-xs text-muted font-mono hover:text-neon transition-colors">Account</a>
<a href={localizePath("/account")} class="text-xs text-muted font-mono hover:text-neon transition-colors">Account</a>
</header>

{#if loading}
Expand Down
17 changes: 17 additions & 0 deletions apps/web/src/components/DomainManager.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,17 @@
}
}

async function deleteDomain(domainName: string) {
if (!user.apiKey) return;
if (!confirm(t("deleteDomainConfirm", { domain: domainName }))) return;
try {
await api.deleteDomain(domainName, user.apiKey);
domains = domains.filter((d) => d.domain !== domainName);
} catch {
// silent
}
}

async function checkDomain(domainName: string) {
if (!user.apiKey) return;
try {
Expand Down Expand Up @@ -124,6 +135,12 @@
{t("checkVerification")}
</button>
{/if}
<button
onclick={() => deleteDomain(domain.domain)}
class="text-[10px] font-mono text-ghost hover:text-ember transition-colors"
>
{t("deleteDomain")}
</button>
</div>
</div>

Expand Down
6 changes: 3 additions & 3 deletions apps/web/src/components/InboxView.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
type MessageSummary,
type MessageDetail as MessageDetailType,
} from "@ephemask/shared";
import { initLocale, useLocale } from "../lib/i18n.svelte";
import { initLocale, useLocale, localizePath } from "../lib/i18n.svelte";
import { useUser, initUser, ensureUser } from "../lib/user.svelte";

const loc = useLocale();
Expand Down Expand Up @@ -280,7 +280,7 @@
{/if}
</div>
<div class="flex items-center gap-3">
<a href="/account" class="text-[10px] font-mono text-ghost hover:text-neon transition-colors">{t("account")}</a>
<a href={localizePath("/account")} class="text-[10px] font-mono text-ghost hover:text-neon transition-colors">{t("account")}</a>
<ThemeToggle />
<LocaleToggle />
</div>
Expand Down Expand Up @@ -309,7 +309,7 @@
<span>{t("inboxExpired")} &mdash; {t("dataDestroyed")}</span>
</div>
<a
href="/"
href={localizePath("/")}
class="mt-3 inline-flex items-center gap-2 px-4 py-2 text-xs font-mono border border-neon/30 text-neon hover:bg-neon/10 transition-all duration-300"
>
<svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
Expand Down
15 changes: 8 additions & 7 deletions apps/web/src/components/LandingPage.svelte
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<script lang="ts">
import { onMount } from "svelte";
import { t, type TranslationKey } from "@ephemask/shared";
import { initLocale, useLocale } from "../lib/i18n.svelte";
import { initLocale, useLocale, localizePath } from "../lib/i18n.svelte";
import { useUser, initUser, ensureUser } from "../lib/user.svelte";
import LocaleToggle from "./LocaleToggle.svelte";
import ThemeToggle from "./ThemeToggle.svelte";
Expand All @@ -20,6 +20,7 @@
await ensureUser();

window.location.href = "/inbox";
// Note: /inbox/[address] is noindex and stays unprefixed by design.
}

const steps = [
Expand Down Expand Up @@ -50,9 +51,9 @@
<a href="#how" class="hover:text-neon transition-colors duration-300">{t("navHowItWorks")}</a>
<a href="#pricing" class="hover:text-neon transition-colors duration-300">{t("navPricing")}</a>
{#if user.isLoggedIn}
<a href="/account" class="hover:text-neon transition-colors duration-300">{t("account")}</a>
<a href={localizePath("/account")} class="hover:text-neon transition-colors duration-300">{t("account")}</a>
{:else}
<a href="/login" class="hover:text-neon transition-colors duration-300">{t("login")}</a>
<a href={localizePath("/login")} class="hover:text-neon transition-colors duration-300">{t("login")}</a>
{/if}
<ThemeToggle />
<LocaleToggle />
Expand Down Expand Up @@ -80,9 +81,9 @@
<a href="#how" onclick={() => menuOpen = false} class="hover:text-neon transition-colors duration-300">{t("navHowItWorks")}</a>
<a href="#pricing" onclick={() => menuOpen = false} class="hover:text-neon transition-colors duration-300">{t("navPricing")}</a>
{#if user.isLoggedIn}
<a href="/account" class="hover:text-neon transition-colors duration-300">{t("account")}</a>
<a href={localizePath("/account")} class="hover:text-neon transition-colors duration-300">{t("account")}</a>
{:else}
<a href="/login" class="hover:text-neon transition-colors duration-300">{t("login")}</a>
<a href={localizePath("/login")} class="hover:text-neon transition-colors duration-300">{t("login")}</a>
{/if}
</nav>
{/if}
Expand Down Expand Up @@ -221,9 +222,9 @@
<span class="text-neon/40">&lt;/&gt;</span>
</div>
<div class="flex justify-center gap-4 text-[10px] font-mono">
<a href="/privacy" class="text-ghost hover:text-neon transition-colors">Privacy Policy</a>
<a href={localizePath("/privacy")} class="text-ghost hover:text-neon transition-colors">Privacy Policy</a>
<span class="text-ghost/30">|</span>
<a href="/terms" class="text-ghost hover:text-neon transition-colors">Terms of Service</a>
<a href={localizePath("/terms")} class="text-ghost hover:text-neon transition-colors">Terms of Service</a>
</div>
</footer>
</div>
Expand Down
9 changes: 7 additions & 2 deletions apps/web/src/components/LocaleToggle.svelte
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<script lang="ts">
import { locales, type Locale } from "@ephemask/shared";
import { useLocale } from "../lib/i18n.svelte";
import { useLocale, stripLocaleFromPath, pathPrefixForLocale } from "../lib/i18n.svelte";

const loc = useLocale();

Expand All @@ -12,7 +12,12 @@
};

function setLang(l: Locale) {
loc.locale = l;
if (l === loc.locale) return;
if (typeof window === "undefined") return;
const stripped = stripLocaleFromPath(window.location.pathname);
const prefix = pathPrefixForLocale(l);
const target = stripped === "/" ? prefix + "/" : prefix + stripped;
window.location.href = (target || "/") + window.location.search + window.location.hash;
}
</script>

Expand Down
6 changes: 3 additions & 3 deletions apps/web/src/components/LoginView.svelte
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<script lang="ts">
import { onMount } from "svelte";
import { t } from "@ephemask/shared";
import { initLocale, useLocale } from "../lib/i18n.svelte";
import { initLocale, useLocale, localizePath } from "../lib/i18n.svelte";
import LocaleToggle from "./LocaleToggle.svelte";

const loc = useLocale();
Expand Down Expand Up @@ -44,7 +44,7 @@
{#key loc.locale}
<div class="min-h-screen flex flex-col">
<header class="px-6 py-5 flex items-center justify-between border-b border-ghost/15">
<a href="/" class="flex items-center gap-2 hover:opacity-80 transition-opacity">
<a href={localizePath("/")} class="flex items-center gap-2 hover:opacity-80 transition-opacity">
<div class="w-2 h-2 rounded-full bg-neon pulse-ring"></div>
<span class="font-display text-xs tracking-widest text-muted uppercase">ephemask</span>
</a>
Expand Down Expand Up @@ -91,7 +91,7 @@
</form>

<div class="mt-4 text-center">
<a href="/account" class="text-[11px] font-mono text-ghost hover:text-neon transition-colors">
<a href={localizePath("/account")} class="text-[11px] font-mono text-ghost hover:text-neon transition-colors">
{t("register")}
</a>
</div>
Expand Down
Loading
Loading