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
16 changes: 16 additions & 0 deletions middleware/01.redirect-to-custom-domain.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { defineMiddleware } from "void";

const LEGACY_HOST = "vp-setup.void.app";
const CANONICAL_ORIGIN = "https://setup.viteplus.dev";

export function buildRedirectTarget(host: string | undefined, requestUrl: string): string | null {
if (host !== LEGACY_HOST) return null;
const url = new URL(requestUrl);
return `${CANONICAL_ORIGIN}${url.pathname}${url.search}`;
}

export default defineMiddleware(async (c, next) => {
const target = buildRedirectTarget(c.req.header("host"), c.req.url);
if (target) return c.redirect(target, 301);
await next();
});
34 changes: 34 additions & 0 deletions tests/redirect.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { describe, expect, it } from "vite-plus/test";
import { buildRedirectTarget } from "../middleware/01.redirect-to-custom-domain";

describe("buildRedirectTarget", () => {
it("redirects legacy host root to canonical origin", () => {
expect(buildRedirectTarget("vp-setup.void.app", "https://vp-setup.void.app/")).toBe(
"https://setup.viteplus.dev/",
);
});

it("preserves path and query string", () => {
expect(
buildRedirectTarget("vp-setup.void.app", "https://vp-setup.void.app/?tag=v1.2.3&arch=arm64"),
).toBe("https://setup.viteplus.dev/?tag=v1.2.3&arch=arm64");
});

it("returns null for the custom domain (no redirect)", () => {
expect(buildRedirectTarget("setup.viteplus.dev", "https://setup.viteplus.dev/")).toBeNull();
});

it("returns null for localhost during dev", () => {
expect(buildRedirectTarget("localhost:5173", "http://localhost:5173/")).toBeNull();
});

it("returns null when host header is missing", () => {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: can the host header ever be null (in a real world scenario)? 👀

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand it doesn't, but hono's API does indeed return undefined https://hono.dev/docs/api/request#header 😢

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mean technically this is possible with HTTP1 😂
And then the server should reject.

expect(buildRedirectTarget(undefined, "https://vp-setup.void.app/")).toBeNull();
});

it("does not match preview/staging subdomains", () => {
expect(
buildRedirectTarget("vp-setup-staging.void.app", "https://vp-setup-staging.void.app/"),
).toBeNull();
});
});
Loading