mirror of
https://github.com/ibelick/ui-skills.git
synced 2026-09-14 18:43:13 +08:00
fix: serve static content through Cloudflare assets
This commit is contained in:
+4
-1
@@ -9,7 +9,10 @@ export default defineConfig({
|
||||
trailingSlash: "ignore",
|
||||
output: "server",
|
||||
session: false,
|
||||
adapter: cloudflare({ imageService: "compile" }),
|
||||
adapter: cloudflare({
|
||||
imageService: "compile",
|
||||
prerenderEnvironment: "node",
|
||||
}),
|
||||
integrations: [react()],
|
||||
vite: {
|
||||
plugins: [tailwindcss()],
|
||||
|
||||
+18
-1
@@ -1,5 +1,7 @@
|
||||
/*
|
||||
Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' https: data:; font-src 'self'; connect-src 'self' https://api.interfaceoffice.com; frame-ancestors 'none'; base-uri 'self'; form-action 'self'; object-src 'none'
|
||||
Cache-Control: public, max-age=0, must-revalidate
|
||||
Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' https: data:; font-src 'self'; connect-src 'self' https://api.interfaceoffice.com https://collector.onedollarstats.com; frame-ancestors 'none'; base-uri 'self'; form-action 'self'; object-src 'none'
|
||||
Link: <https://www.ui-skills.com/llms.txt>; rel="describedby"; type="text/plain"; title="Site index for LLMs", <https://www.ui-skills.com/.well-known/api-catalog>; rel="api-catalog"; type="application/linkset+json"; title="API catalog", <https://www.ui-skills.com/.well-known/agent-skills/index.json>; rel="describedby"; type="application/json"; title="Agent skills index", <https://www.ui-skills.com/.well-known/mcp/server-card.json>; rel="service-desc"; type="application/json"; title="MCP server card", <https://www.ui-skills.com/.well-known/oauth-protected-resource>; rel="oauth-protected-resource"; type="application/json"; title="OAuth protected resource", <https://www.ui-skills.com/auth.md>; rel="service-doc"; type="text/markdown"; title="Agent auth", <https://www.ui-skills.com/design.md>; rel="service-doc"; type="text/markdown"; title="Design system", <https://www.ui-skills.com/skills/registry.json>; rel="service-desc"; type="application/json"; title="Skills registry", <https://www.ui-skills.com/sitemap.xml>; rel="sitemap"; type="application/xml"; title="Sitemap"
|
||||
Permissions-Policy: camera=(), geolocation=(), microphone=()
|
||||
Referrer-Policy: strict-origin-when-cross-origin
|
||||
X-Content-Type-Options: nosniff
|
||||
@@ -10,3 +12,18 @@
|
||||
|
||||
/analytics/*
|
||||
Cache-Control: public, max-age=3600
|
||||
|
||||
/_astro/*
|
||||
Cache-Control: public, max-age=31536000, immutable
|
||||
|
||||
/.well-known/api-catalog
|
||||
Content-Type: application/linkset+json; charset=utf-8
|
||||
|
||||
/.well-known/oauth-authorization-server
|
||||
Content-Type: application/json; charset=utf-8
|
||||
|
||||
/.well-known/oauth-protected-resource
|
||||
Content-Type: application/json; charset=utf-8
|
||||
|
||||
/.well-known/openid-configuration
|
||||
Content-Type: application/json; charset=utf-8
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
/agent /agents 301
|
||||
/mcp/server /mcp/docs 302
|
||||
/agent/* /agents/:splat 301
|
||||
@@ -118,7 +118,7 @@ const commandItems = [
|
||||
script.src = "/analytics/stonks.js";
|
||||
script.defer = true;
|
||||
script.dataset.hostname = "ui-skills.com";
|
||||
script.dataset.url = "/analytics/events";
|
||||
script.dataset.url = "https://collector.onedollarstats.com/events";
|
||||
document.head.appendChild(script);
|
||||
};
|
||||
if ("requestIdleCallback" in window) {
|
||||
|
||||
+7
-7
@@ -6,7 +6,7 @@ import { maybeNegotiateMarkdown } from "./lib/markdown-negotiation";
|
||||
|
||||
const securityHeaders = {
|
||||
"Content-Security-Policy":
|
||||
"default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' https: data:; font-src 'self'; connect-src 'self' https://api.interfaceoffice.com; frame-ancestors 'none'; base-uri 'self'; form-action 'self'; object-src 'none'",
|
||||
"default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' https: data:; font-src 'self'; connect-src 'self' https://api.interfaceoffice.com https://collector.onedollarstats.com; frame-ancestors 'none'; base-uri 'self'; form-action 'self'; object-src 'none'",
|
||||
"Permissions-Policy": "camera=(), geolocation=(), microphone=()",
|
||||
"Referrer-Policy": "strict-origin-when-cross-origin",
|
||||
"X-Content-Type-Options": "nosniff",
|
||||
@@ -38,9 +38,14 @@ function applyResponseHeaders(
|
||||
}
|
||||
|
||||
export const onRequest = defineMiddleware(async (context, next) => {
|
||||
const url = new URL(context.request.url);
|
||||
const origin = getSiteOrigin(context.site);
|
||||
|
||||
if (context.isPrerendered) {
|
||||
return next();
|
||||
}
|
||||
|
||||
const url = new URL(context.request.url);
|
||||
|
||||
if (url.pathname !== "/" && url.pathname.endsWith("/")) {
|
||||
url.pathname = url.pathname.replace(/\/+$/, "");
|
||||
const status =
|
||||
@@ -52,11 +57,6 @@ export const onRequest = defineMiddleware(async (context, next) => {
|
||||
}
|
||||
|
||||
const response = await next();
|
||||
|
||||
if (context.isPrerendered) {
|
||||
return response;
|
||||
}
|
||||
|
||||
const withHeaders = applyResponseHeaders(response, origin, url.pathname);
|
||||
return maybeNegotiateMarkdown(context.request, withHeaders);
|
||||
});
|
||||
|
||||
@@ -3,9 +3,17 @@ import type { APIRoute } from "astro";
|
||||
import {
|
||||
defaultSkillContentLoader,
|
||||
getRegistrySkillByDiscoveryName,
|
||||
toDiscoveryName,
|
||||
} from "../../../../lib/agent-skills-discovery";
|
||||
import { registry } from "../../../../data/registry";
|
||||
|
||||
export const prerender = false;
|
||||
export const prerender = true;
|
||||
|
||||
export function getStaticPaths() {
|
||||
return registry.map((entry) => ({
|
||||
params: { skill: toDiscoveryName(entry.pathSlug) },
|
||||
}));
|
||||
}
|
||||
|
||||
export const GET: APIRoute = async ({ params }) => {
|
||||
const slug = params.skill;
|
||||
|
||||
@@ -7,6 +7,8 @@ import {
|
||||
} from "../../../lib/agent-skills-discovery";
|
||||
import { getSiteOrigin } from "../../../lib/agent-discovery";
|
||||
|
||||
export const prerender = true;
|
||||
|
||||
export const GET: APIRoute = async ({ site }) => {
|
||||
const origin = getSiteOrigin(site);
|
||||
const index = toPublicAgentSkillsIndex(await buildAgentSkillsIndex(origin));
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import type { APIRoute } from "astro";
|
||||
|
||||
export const prerender = true;
|
||||
|
||||
import {
|
||||
API_CATALOG_PATH,
|
||||
apiCatalogContentType,
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import type { APIRoute } from "astro";
|
||||
|
||||
export const prerender = true;
|
||||
|
||||
import { buildJwks, jsonHeaders } from "../../lib/oauth-discovery";
|
||||
|
||||
export const GET: APIRoute = () =>
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import type { APIRoute } from "astro";
|
||||
|
||||
export const prerender = true;
|
||||
|
||||
import {
|
||||
buildMcpServerCard,
|
||||
discoveryJsonHeaders,
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import type { APIRoute } from "astro";
|
||||
|
||||
export const prerender = true;
|
||||
|
||||
import {
|
||||
buildAuthorizationServerMetadata,
|
||||
getSiteOrigin,
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import type { APIRoute } from "astro";
|
||||
|
||||
export const prerender = true;
|
||||
|
||||
import {
|
||||
buildProtectedResourceMetadata,
|
||||
getSiteOrigin,
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import type { APIRoute } from "astro";
|
||||
|
||||
export const prerender = true;
|
||||
|
||||
import {
|
||||
buildOpenIdConfiguration,
|
||||
getSiteOrigin,
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
---
|
||||
export const prerender = true;
|
||||
|
||||
import Layout from "../layouts/Layout.astro";
|
||||
|
||||
Astro.response.status = 404;
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
---
|
||||
import { agents } from "../../data/agents";
|
||||
|
||||
export const prerender = true;
|
||||
|
||||
export function getStaticPaths() {
|
||||
return agents.map((agent) => ({ params: { id: agent.id } }));
|
||||
}
|
||||
|
||||
return Astro.redirect(`/agents/${Astro.params.id ?? ""}`, 301);
|
||||
---
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
---
|
||||
export const prerender = true;
|
||||
|
||||
return Astro.redirect("/agents", 301);
|
||||
---
|
||||
|
||||
@@ -2,10 +2,15 @@
|
||||
import Layout from "../../layouts/Layout.astro";
|
||||
import AgentStartHere from "../../ui/agent-start-here.astro";
|
||||
|
||||
export const prerender = false;
|
||||
import Breadcrumbs from "../../ui/breadcrumbs.astro";
|
||||
import { agentById, agents } from "../../data/agents";
|
||||
|
||||
export const prerender = true;
|
||||
|
||||
export function getStaticPaths() {
|
||||
return agents.map((agent) => ({ params: { id: agent.id } }));
|
||||
}
|
||||
|
||||
const agentId = Astro.params.id ?? "";
|
||||
const agent = agentById.get(agentId);
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
---
|
||||
export const prerender = true;
|
||||
|
||||
import Layout from "../../layouts/Layout.astro";
|
||||
import Breadcrumbs from "../../ui/breadcrumbs.astro";
|
||||
import { agents } from "../../data/agents";
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
import type { APIRoute } from "astro";
|
||||
|
||||
const COLLECTOR_URL = "https://collector.onedollarstats.com/events";
|
||||
|
||||
const responseHeaders = {
|
||||
"Cache-Control": "no-store",
|
||||
};
|
||||
|
||||
export const GET: APIRoute = async ({ request }) => {
|
||||
const incoming = new URL(request.url);
|
||||
const target = new URL(COLLECTOR_URL);
|
||||
target.search = incoming.search;
|
||||
|
||||
const response = await fetch(target.toString(), {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"User-Agent":
|
||||
request.headers.get("user-agent") ?? "ui-skills-analytics-proxy",
|
||||
},
|
||||
});
|
||||
|
||||
return new Response(null, {
|
||||
status: response.status,
|
||||
headers: responseHeaders,
|
||||
});
|
||||
};
|
||||
|
||||
export const POST: APIRoute = async ({ request }) => {
|
||||
const body = await request.text();
|
||||
const response = await fetch(COLLECTOR_URL, {
|
||||
method: "POST",
|
||||
body,
|
||||
headers: {
|
||||
"Content-Type":
|
||||
request.headers.get("content-type") ?? "application/json",
|
||||
},
|
||||
});
|
||||
|
||||
return new Response(await response.text(), {
|
||||
status: response.status,
|
||||
headers: {
|
||||
...responseHeaders,
|
||||
"Content-Type": response.headers.get("content-type") ?? "text/plain",
|
||||
},
|
||||
});
|
||||
};
|
||||
@@ -1,5 +1,7 @@
|
||||
import type { APIRoute } from "astro";
|
||||
|
||||
export const prerender = true;
|
||||
|
||||
import {
|
||||
buildAuthMarkdown,
|
||||
getSiteOrigin,
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
---
|
||||
export const prerender = true;
|
||||
|
||||
import Layout from "../layouts/Layout.astro";
|
||||
import Breadcrumbs from "../ui/breadcrumbs.astro";
|
||||
import CodeBlock from "../ui/code-block.astro";
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import type { APIRoute } from "astro";
|
||||
|
||||
export const prerender = true;
|
||||
import designDocument from "../../DESIGN.md?raw";
|
||||
|
||||
import { SEMI_STATIC_CACHE } from "../lib/cache-headers";
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
---
|
||||
export const prerender = true;
|
||||
|
||||
import Layout from "../layouts/Layout.astro";
|
||||
import { playbook } from "../data/playbook";
|
||||
import { skills } from "../data/skills";
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import type { APIRoute } from "astro";
|
||||
|
||||
export const prerender = true;
|
||||
import { skills } from "../data/skills";
|
||||
|
||||
import { SEMI_STATIC_CACHE } from "../lib/cache-headers";
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
---
|
||||
export const prerender = true;
|
||||
|
||||
import Layout from "../../layouts/Layout.astro";
|
||||
import Breadcrumbs from "../../ui/breadcrumbs.astro";
|
||||
import CodeBlock from "../../ui/code-block.astro";
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
---
|
||||
export const prerender = true;
|
||||
|
||||
return Astro.redirect("/mcp/docs");
|
||||
---
|
||||
|
||||
@@ -7,7 +7,11 @@ import SkillCard from "../../ui/skill-card.astro";
|
||||
import { playbook, playbookBySlug } from "../../data/playbook.ts";
|
||||
import { skills } from "../../data/skills.ts";
|
||||
|
||||
export const prerender = false;
|
||||
export const prerender = true;
|
||||
|
||||
export function getStaticPaths() {
|
||||
return playbook.map((entry) => ({ params: { slug: entry.slug } }));
|
||||
}
|
||||
|
||||
const routeSlug = Astro.params.slug ?? "";
|
||||
const slug = Array.isArray(routeSlug) ? routeSlug.join("/") : routeSlug;
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
---
|
||||
export const prerender = true;
|
||||
|
||||
import Layout from "../../layouts/Layout.astro";
|
||||
import Breadcrumbs from "../../ui/breadcrumbs.astro";
|
||||
import PlaybookCard from "../../ui/playbook-card.astro";
|
||||
|
||||
@@ -2,6 +2,8 @@ import type { APIRoute } from "astro";
|
||||
|
||||
import { SEMI_STATIC_CACHE } from "../lib/cache-headers";
|
||||
|
||||
export const prerender = true;
|
||||
|
||||
const SITE_URL = "https://www.ui-skills.com";
|
||||
|
||||
export const GET: APIRoute = ({ site }) => {
|
||||
|
||||
@@ -5,6 +5,8 @@ import { skills, type Skill } from "../data/skills";
|
||||
import { agents } from "../data/agents";
|
||||
import { playbook } from "../data/playbook";
|
||||
|
||||
export const prerender = true;
|
||||
|
||||
const SITE_URL = "https://www.ui-skills.com";
|
||||
|
||||
const escapeXml = (value: string) =>
|
||||
|
||||
@@ -20,11 +20,16 @@ import {
|
||||
getSkillsBySlug,
|
||||
getSkillsBySource,
|
||||
getSkillsByTopic,
|
||||
getSkillRoutePaths,
|
||||
} from "../../lib/skill-catalog";
|
||||
import { defaultSkillContentLoader } from "../../lib/agent-skills-discovery";
|
||||
import { playbookBySkillSlug } from "../../data/playbook.ts";
|
||||
|
||||
export const prerender = false;
|
||||
export const prerender = true;
|
||||
|
||||
export function getStaticPaths() {
|
||||
return getSkillRoutePaths();
|
||||
}
|
||||
|
||||
const titleize = (value: string) =>
|
||||
value
|
||||
|
||||
@@ -1,7 +1,17 @@
|
||||
import type { APIRoute } from "astro";
|
||||
import { getRegistryByPath, getSkillByPath } from "../../../lib/skill-catalog";
|
||||
import {
|
||||
getCanonicalSkillPaths,
|
||||
getRegistryByPath,
|
||||
getSkillByPath,
|
||||
} from "../../../lib/skill-catalog";
|
||||
import { defaultSkillContentLoader } from "../../../lib/agent-skills-discovery";
|
||||
|
||||
export const prerender = true;
|
||||
|
||||
export function getStaticPaths() {
|
||||
return getCanonicalSkillPaths();
|
||||
}
|
||||
|
||||
export const GET: APIRoute = async ({ params }) => {
|
||||
const routeSlug = params.slug ?? "";
|
||||
const pathSlug = Array.isArray(routeSlug) ? routeSlug.join("/") : routeSlug;
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
---
|
||||
export const prerender = true;
|
||||
|
||||
import Layout from "../../layouts/Layout.astro";
|
||||
import Breadcrumbs from "../../ui/breadcrumbs.astro";
|
||||
import { skills } from "../../data/skills";
|
||||
|
||||
@@ -5,6 +5,8 @@ import { topics } from "../../data/topics";
|
||||
|
||||
import { REGISTRY_JSON_CACHE } from "../../lib/cache-headers";
|
||||
|
||||
export const prerender = true;
|
||||
|
||||
export const GET: APIRoute = () => {
|
||||
return new Response(JSON.stringify({ registry, topics }), {
|
||||
headers: {
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import type { APIRoute } from "astro";
|
||||
|
||||
export const prerender = true;
|
||||
|
||||
import type { RegistrySkill } from "../../data/registry";
|
||||
import { registry } from "../../data/registry";
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
---
|
||||
export const prerender = true;
|
||||
|
||||
import Layout from "../../layouts/Layout.astro";
|
||||
import Breadcrumbs from "../../ui/breadcrumbs.astro";
|
||||
import TopicTag from "../../ui/topic-tag.astro";
|
||||
|
||||
+40
-23
@@ -16,7 +16,17 @@ const port =
|
||||
}));
|
||||
const server = spawn(
|
||||
"npx",
|
||||
["wrangler", "dev", "--local", "--ip", "127.0.0.1", "--port", `${port}`],
|
||||
[
|
||||
"wrangler",
|
||||
"dev",
|
||||
"--config",
|
||||
"dist/server/wrangler.json",
|
||||
"--local",
|
||||
"--ip",
|
||||
"127.0.0.1",
|
||||
"--port",
|
||||
`${port}`,
|
||||
],
|
||||
{
|
||||
stdio: ["ignore", "pipe", "pipe"],
|
||||
},
|
||||
@@ -94,8 +104,12 @@ try {
|
||||
if (!homepageBody.includes('href="/mcp/docs"')) {
|
||||
throw new Error("Homepage is missing the MCP guide link");
|
||||
}
|
||||
if (!homepageBody.includes('dataset.url = "/analytics/events"')) {
|
||||
throw new Error("Homepage is missing the One Dollar Stats proxy loader");
|
||||
if (
|
||||
!homepageBody.includes(
|
||||
'dataset.url = "https://collector.onedollarstats.com/events"',
|
||||
)
|
||||
) {
|
||||
throw new Error("Homepage is missing the direct One Dollar Stats collector");
|
||||
}
|
||||
if (!homepageBody.includes('script.src = "/analytics/stonks.js"')) {
|
||||
throw new Error("Homepage is missing the first-party One Dollar Stats loader");
|
||||
@@ -117,8 +131,8 @@ try {
|
||||
e: [{ t: "PageView" }],
|
||||
}),
|
||||
});
|
||||
if (analyticsProxy.status === 404) {
|
||||
throw new Error("Analytics proxy route is missing");
|
||||
if (![404, 405].includes(analyticsProxy.status)) {
|
||||
throw new Error("Analytics proxy route is still active");
|
||||
}
|
||||
|
||||
const mcpDocs = await fetchLocal("/mcp/docs");
|
||||
@@ -140,6 +154,24 @@ try {
|
||||
throw new Error("MCP docs code blocks are missing Shiki theme colors");
|
||||
}
|
||||
|
||||
const legacyAgent = await fetchLocal("/agent/claude-code", {
|
||||
redirect: "manual",
|
||||
});
|
||||
if (
|
||||
legacyAgent.status !== 301 ||
|
||||
legacyAgent.headers.get("location") !== "/agents/claude-code"
|
||||
) {
|
||||
throw new Error("Legacy agent route did not return its static redirect");
|
||||
}
|
||||
|
||||
const legacyMcp = await fetchLocal("/mcp/server", { redirect: "manual" });
|
||||
if (
|
||||
legacyMcp.status !== 302 ||
|
||||
legacyMcp.headers.get("location") !== "/mcp/docs"
|
||||
) {
|
||||
throw new Error("Legacy MCP route did not return its static redirect");
|
||||
}
|
||||
|
||||
const cliDocs = await fetchLocal("/cli");
|
||||
if (cliDocs.status !== 200) {
|
||||
throw new Error(`CLI docs returned ${cliDocs.status}`);
|
||||
@@ -165,24 +197,6 @@ try {
|
||||
throw new Error(`API catalog returned unexpected type: ${apiCatalogType}`);
|
||||
}
|
||||
|
||||
const markdownHomepage = await fetchLocal("/", {
|
||||
headers: { Accept: "text/markdown" },
|
||||
});
|
||||
if (markdownHomepage.status !== 200) {
|
||||
throw new Error(`Markdown homepage returned ${markdownHomepage.status}`);
|
||||
}
|
||||
const markdownType = markdownHomepage.headers.get("content-type") ?? "";
|
||||
if (!markdownType.startsWith("text/markdown")) {
|
||||
throw new Error(`Markdown homepage content-type was ${markdownType}`);
|
||||
}
|
||||
if (markdownHomepage.headers.get("x-markdown-tokens") === null) {
|
||||
throw new Error("Markdown homepage is missing x-markdown-tokens");
|
||||
}
|
||||
const markdownBody = await markdownHomepage.text();
|
||||
if (!markdownBody.trim()) {
|
||||
throw new Error("Markdown homepage body was empty");
|
||||
}
|
||||
|
||||
const registry = await fetchLocal("/skills/registry.json");
|
||||
if (registry.status !== 200) {
|
||||
throw new Error(`Registry returned ${registry.status}`);
|
||||
@@ -201,6 +215,9 @@ try {
|
||||
if (mcp.status !== 200 || !(await mcp.text()).includes('"list_skills"')) {
|
||||
throw new Error(`MCP tools/list failed with ${mcp.status}`);
|
||||
}
|
||||
if (mcp.headers.get("x-content-type-options") !== "nosniff") {
|
||||
throw new Error("MCP response is missing dynamic security headers");
|
||||
}
|
||||
|
||||
const invalidMcp = await fetchLocal("/mcp", {
|
||||
method: "POST",
|
||||
|
||||
+8
-1
@@ -5,6 +5,13 @@
|
||||
"assets": {
|
||||
"directory": "./dist",
|
||||
"binding": "ASSETS",
|
||||
"run_worker_first": false,
|
||||
"not_found_handling": "404-page",
|
||||
"run_worker_first": [
|
||||
"/api/*",
|
||||
"/mcp",
|
||||
"/oauth/*",
|
||||
"/_server-islands/*",
|
||||
"/_image",
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user