mirror of
https://github.com/anomalyco/opencode.git
synced 2026-07-21 10:16:03 +00:00
fix(data): canonicalize locale pages
This commit is contained in:
@@ -1,10 +1,15 @@
|
||||
import type { APIEvent } from "@solidjs/start/server"
|
||||
import { Resource } from "@opencode-ai/console-resource"
|
||||
import { LOCALE_HEADER, cookie, localeFromRequest, route, tag } from "~/lib/language"
|
||||
|
||||
const dataPath = "/data"
|
||||
|
||||
export async function statsProxy(evt: APIEvent) {
|
||||
const req = evt.request.clone()
|
||||
const locale = localeFromRequest(req)
|
||||
const redirect = redirectToLocalizedData(req, new URL(req.url), locale)
|
||||
if (redirect) return redirect
|
||||
|
||||
const targetUrl = new URL(req.url)
|
||||
targetUrl.protocol = "https:"
|
||||
targetUrl.hostname = Resource.App.stage === "production" ? "stats.opencode.ai" : "stats.dev.opencode.ai"
|
||||
@@ -18,9 +23,13 @@ export async function statsProxy(evt: APIEvent) {
|
||||
targetUrl.pathname = targetUrl.pathname.slice(dataPath.length)
|
||||
}
|
||||
|
||||
const requestHeaders = new Headers(req.headers)
|
||||
requestHeaders.set(LOCALE_HEADER, locale)
|
||||
requestHeaders.set("accept-language", tag(locale))
|
||||
|
||||
const response = await fetch(targetUrl, {
|
||||
method: req.method,
|
||||
headers: req.headers,
|
||||
headers: requestHeaders,
|
||||
body: req.body,
|
||||
})
|
||||
|
||||
@@ -30,6 +39,7 @@ export async function statsProxy(evt: APIEvent) {
|
||||
headers.delete("content-encoding")
|
||||
headers.delete("content-length")
|
||||
headers.delete("etag")
|
||||
appendVary(headers, "Accept-Language", "Cookie")
|
||||
|
||||
return new Response(rewriteStatsHtml(await response.text()), {
|
||||
status: response.status,
|
||||
@@ -52,3 +62,60 @@ export function statsRedirect(evt: APIEvent) {
|
||||
function rewriteStatsHtml(html: string) {
|
||||
return html.replaceAll('"/_build/', `"${dataPath}/_build/`).replaceAll("'/_build/", `'${dataPath}/_build/`)
|
||||
}
|
||||
|
||||
function redirectToLocalizedData(request: Request, url: URL, locale: ReturnType<typeof localeFromRequest>) {
|
||||
if (locale === "en") return null
|
||||
if (request.headers.get(LOCALE_HEADER)) return null
|
||||
if (request.method !== "GET" && request.method !== "HEAD") return null
|
||||
if (!acceptsHtml(request)) return null
|
||||
if (!url.pathname.startsWith(`${dataPath}/`) && url.pathname !== dataPath) return null
|
||||
if (isDataBypassPath(url.pathname)) return null
|
||||
|
||||
const next = new URL(url)
|
||||
next.pathname = route(locale, url.pathname)
|
||||
|
||||
const headers = new Headers({
|
||||
Location: next.toString(),
|
||||
})
|
||||
headers.append("set-cookie", cookie(locale))
|
||||
appendVary(headers, "Accept-Language", "Cookie")
|
||||
|
||||
return new Response(null, {
|
||||
status: 308,
|
||||
headers,
|
||||
})
|
||||
}
|
||||
|
||||
function acceptsHtml(request: Request) {
|
||||
const accept = request.headers.get("accept")
|
||||
return !accept || accept.includes("text/html") || accept.includes("*/*")
|
||||
}
|
||||
|
||||
function isDataBypassPath(pathname: string) {
|
||||
return (
|
||||
pathname.startsWith(`${dataPath}/_build/`) ||
|
||||
pathname.startsWith(`${dataPath}/api/`) ||
|
||||
pathname.startsWith(`${dataPath}/_server`) ||
|
||||
pathname === `${dataPath}/banner.jpg` ||
|
||||
pathname === `${dataPath}/banner.png`
|
||||
)
|
||||
}
|
||||
|
||||
function appendVary(headers: Headers, ...values: string[]) {
|
||||
const existing = headers
|
||||
.get("vary")
|
||||
?.split(",")
|
||||
.map((value) => value.trim())
|
||||
.filter(Boolean)
|
||||
|
||||
headers.set(
|
||||
"vary",
|
||||
values
|
||||
.reduce(
|
||||
(result, value) =>
|
||||
result.some((item) => item.toLowerCase() === value.toLowerCase()) ? result : [...result, value],
|
||||
existing ?? [],
|
||||
)
|
||||
.join(", "),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ import {
|
||||
type ModelCatalogEntry,
|
||||
} from "../model-catalog"
|
||||
import { runStatsEffect } from "../../stats-runtime"
|
||||
import { setStatsPageCacheHeaders } from "../stats-cache"
|
||||
import {
|
||||
applyThemePreference,
|
||||
Footer,
|
||||
@@ -86,7 +87,7 @@ export default function StatsModel() {
|
||||
const i18n = useI18n()
|
||||
const language = useLanguage()
|
||||
const event = getRequestEvent()
|
||||
event?.response.headers.set("Cache-Control", "public, max-age=60, s-maxage=300, stale-while-revalidate=86400")
|
||||
setStatsPageCacheHeaders(event?.response.headers)
|
||||
const params = useParams()
|
||||
const labParam = createMemo(() => params.lab ?? "")
|
||||
const modelParam = createMemo(() => params.model ?? "")
|
||||
|
||||
@@ -21,6 +21,7 @@ import {
|
||||
type ModelCatalogLab,
|
||||
} from "../model-catalog"
|
||||
import { runStatsEffect } from "../../stats-runtime"
|
||||
import { setStatsPageCacheHeaders } from "../stats-cache"
|
||||
import {
|
||||
applyThemePreference,
|
||||
Footer,
|
||||
@@ -43,7 +44,7 @@ export default function StatsLab() {
|
||||
const i18n = useI18n()
|
||||
const language = useLanguage()
|
||||
const event = getRequestEvent()
|
||||
event?.response.headers.set("Cache-Control", "public, max-age=60, s-maxage=300, stale-while-revalidate=86400")
|
||||
setStatsPageCacheHeaders(event?.response.headers)
|
||||
const params = useParams()
|
||||
const labParam = createMemo(() => params.lab ?? "")
|
||||
const catalog = createAsync(() => getModelCatalog())
|
||||
|
||||
@@ -32,6 +32,7 @@ import { useI18n } from "../context/i18n"
|
||||
import { useLanguage } from "../context/language"
|
||||
import { localizedUrl } from "../lib/language"
|
||||
import { findModelCatalogEntry, getModelCatalog, type ModelCatalog } from "./model-catalog"
|
||||
import { setStatsPageCacheHeaders } from "./stats-cache"
|
||||
import {
|
||||
applyThemePreference,
|
||||
Footer,
|
||||
@@ -121,7 +122,7 @@ export default function StatsHome() {
|
||||
const i18n = useI18n()
|
||||
const language = useLanguage()
|
||||
const event = getRequestEvent()
|
||||
event?.response.headers.set("Cache-Control", "public, max-age=60, s-maxage=300, stale-while-revalidate=86400")
|
||||
setStatsPageCacheHeaders(event?.response.headers)
|
||||
const statsHomeUrl = localizedUrl(language.locale(), "/data/")
|
||||
const statsUnfurlUrl = new URL(statsUnfurlPath, localizedUrl("en", "/data/")).toString()
|
||||
const data = createAsync(() => getData())
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
const statsPageCacheControl = "public, max-age=60, s-maxage=300, stale-while-revalidate=86400"
|
||||
|
||||
export function setStatsPageCacheHeaders(headers: Headers | undefined) {
|
||||
if (!headers) return
|
||||
|
||||
headers.set("Cache-Control", statsPageCacheControl)
|
||||
appendVary(headers, "Accept-Language", "Cookie")
|
||||
}
|
||||
|
||||
function appendVary(headers: Headers, ...values: string[]) {
|
||||
const existing = headers
|
||||
.get("vary")
|
||||
?.split(",")
|
||||
.map((value) => value.trim())
|
||||
.filter(Boolean)
|
||||
|
||||
headers.set(
|
||||
"vary",
|
||||
values
|
||||
.reduce(
|
||||
(result, value) =>
|
||||
result.some((item) => item.toLowerCase() === value.toLowerCase()) ? result : [...result, value],
|
||||
existing ?? [],
|
||||
)
|
||||
.join(", "),
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user