feat(website): add SEO, sitemap, redirects, CI workflow, and Vercel config (#10156)

## Summary

<!-- One sentence describing what changed and why. -->

## Changes

- **What**: <!-- Core functionality added/modified -->
- **Breaking**: <!-- Any breaking changes (if none, remove this line)
-->
- **Dependencies**: <!-- New dependencies (if none, remove this line)
-->

## Review Focus

<!-- Critical design decisions or edge cases that need attention -->

<!-- If this PR fixes an issue, uncomment and update the line below -->
<!-- Fixes #ISSUE_NUMBER -->

## Screenshots (if applicable)

<!-- Add screenshots or video recording to help explain your changes -->

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-10156-feat-website-add-SEO-sitemap-redirects-CI-workflow-and-Vercel-config-3266d73d3650816ab9eaebd11072d481)
by [Unito](https://www.unito.io)

---------

Co-authored-by: GitHub Action <action@github.com>
This commit is contained in:
Christian Byrne
2026-04-06 18:20:28 -07:00
committed by GitHub
parent 0a5f281291
commit 32ff1a5bdb
13 changed files with 154 additions and 1 deletions

33
.github/workflows/ci-website-build.yaml vendored Normal file
View File

@@ -0,0 +1,33 @@
# Description: Build and validate the marketing website (apps/website)
name: 'CI: Website Build'
on:
push:
branches: [main, master, website/*]
paths:
- 'apps/website/**'
- 'packages/design-system/**'
- 'pnpm-lock.yaml'
pull_request:
branches-ignore: [wip/*, draft/*, temp/*]
paths:
- 'apps/website/**'
- 'packages/design-system/**'
- 'pnpm-lock.yaml'
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Setup frontend
uses: ./.github/actions/setup-frontend
- name: Build website
run: pnpm --filter @comfyorg/website build

View File

@@ -1,11 +1,12 @@
import { defineConfig } from 'astro/config'
import sitemap from '@astrojs/sitemap'
import vue from '@astrojs/vue'
import tailwindcss from '@tailwindcss/vite'
export default defineConfig({
site: 'https://comfy.org',
output: 'static',
integrations: [vue()],
integrations: [vue(), sitemap()],
vite: {
plugins: [tailwindcss()]
},

View File

@@ -9,6 +9,7 @@
"preview": "astro preview"
},
"dependencies": {
"@astrojs/sitemap": "catalog:",
"@comfyorg/design-system": "workspace:*",
"@vercel/analytics": "catalog:",
"vue": "catalog:"

View File

@@ -0,0 +1,4 @@
User-agent: *
Allow: /
Sitemap: https://comfy.org/sitemap-index.xml

View File

@@ -96,6 +96,10 @@ const socials = [
v-for="link in column.links"
:key="link.href"
:href="link.href"
:target="link.href.startsWith('http') ? '_blank' : undefined"
:rel="
link.href.startsWith('http') ? 'noopener noreferrer' : undefined
"
class="text-sm text-smoke-700 transition-colors hover:text-white"
>
{{ link.label }}

View File

@@ -7,12 +7,14 @@ interface Props {
title: string
description?: string
ogImage?: string
noindex?: boolean
}
const {
title,
description = 'Comfy is the AI creation engine for visual professionals who demand control.',
ogImage = '/og-default.png',
noindex = false,
} = Astro.props
const siteBase = Astro.site ?? 'https://comfy.org'
@@ -21,6 +23,29 @@ const ogImageURL = new URL(ogImage, siteBase)
const locale = Astro.currentLocale ?? 'en'
const gtmId = 'GTM-NP9JM6K7'
const gtmEnabled = import.meta.env.PROD
const organizationJsonLd = {
'@context': 'https://schema.org',
'@type': 'Organization',
name: 'Comfy Org',
url: 'https://comfy.org',
logo: 'https://comfy.org/favicon.svg',
sameAs: [
'https://github.com/comfyanonymous/ComfyUI',
'https://discord.gg/comfyorg',
'https://x.com/comaboratory',
'https://reddit.com/r/comfyui',
'https://linkedin.com/company/comfyorg',
'https://instagram.com/comfyorg',
],
}
const websiteJsonLd = {
'@context': 'https://schema.org',
'@type': 'WebSite',
name: 'Comfy',
url: 'https://comfy.org',
}
---
<!doctype html>
@@ -29,26 +54,36 @@ const gtmEnabled = import.meta.env.PROD
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="description" content={description} />
{noindex && <meta name="robots" content="noindex, nofollow" />}
<title>{title}</title>
<link rel="icon" href="/favicon.svg" type="image/svg+xml" />
<link rel="canonical" href={canonicalURL.href} />
<link rel="preconnect" href="https://www.googletagmanager.com" />
<link rel="dns-prefetch" href="https://www.googletagmanager.com" />
<!-- Open Graph -->
<meta property="og:type" content="website" />
<meta property="og:title" content={title} />
<meta property="og:description" content={description} />
<meta property="og:image" content={ogImageURL.href} />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta property="og:url" content={canonicalURL.href} />
<meta property="og:locale" content={locale} />
<meta property="og:site_name" content="Comfy" />
<!-- Twitter -->
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:site" content="@comaboratory" />
<meta name="twitter:title" content={title} />
<meta name="twitter:description" content={description} />
<meta name="twitter:image" content={ogImageURL.href} />
<!-- Structured Data -->
<script type="application/ld+json" set:html={JSON.stringify(organizationJsonLd)} />
<script type="application/ld+json" set:html={JSON.stringify(websiteJsonLd)} />
<!-- Google Tag Manager -->
{gtmEnabled && (
<script is:inline define:vars={{ gtmId }}>

View File

@@ -7,6 +7,7 @@ import SiteFooter from '../components/SiteFooter.vue'
<BaseLayout
title="Privacy Policy — Comfy"
description="Comfy privacy policy. Learn how we collect, use, and protect your personal information."
noindex
>
<SiteNav client:load />
<main class="mx-auto max-w-3xl px-6 py-24">

View File

@@ -7,6 +7,7 @@ import SiteFooter from '../components/SiteFooter.vue'
<BaseLayout
title="Terms of Service — Comfy"
description="Terms of Service for ComfyUI and related Comfy services."
noindex
>
<SiteNav client:load />
<main class="mx-auto max-w-3xl px-6 py-24 sm:py-32">

View File

@@ -7,6 +7,7 @@ import SiteFooter from '../../components/SiteFooter.vue'
<BaseLayout
title="隐私政策 — Comfy"
description="Comfy 隐私政策。了解我们如何收集、使用和保护您的个人信息。"
noindex
>
<SiteNav client:load />
<main class="mx-auto max-w-3xl px-6 py-24">

View File

@@ -7,6 +7,7 @@ import SiteFooter from '../../components/SiteFooter.vue'
<BaseLayout
title="服务条款 — Comfy"
description="ComfyUI 及相关 Comfy 服务的服务条款。"
noindex
>
<SiteNav client:load />
<main class="mx-auto max-w-3xl px-6 py-24 sm:py-32">

26
apps/website/vercel.json Normal file
View File

@@ -0,0 +1,26 @@
{
"$schema": "https://openapi.vercel.sh/vercel.json",
"buildCommand": "pnpm --filter @comfyorg/website build",
"outputDirectory": "apps/website/dist",
"installCommand": "pnpm install --frozen-lockfile",
"framework": null,
"redirects": [
{
"source": "/pricing",
"destination": "/cloud/pricing",
"permanent": true
},
{
"source": "/enterprise",
"destination": "/cloud/enterprise",
"permanent": true
},
{
"source": "/blog",
"destination": "https://blog.comfy.org/",
"permanent": true
},
{ "source": "/contact", "destination": "/about", "permanent": true },
{ "source": "/press", "destination": "/about", "permanent": true }
]
}

44
pnpm-lock.yaml generated
View File

@@ -9,6 +9,9 @@ catalogs:
'@alloc/quick-lru':
specifier: ^5.2.0
version: 5.2.0
'@astrojs/sitemap':
specifier: ^3.7.1
version: 3.7.1
'@astrojs/vue':
specifier: ^5.0.0
version: 5.1.4
@@ -910,6 +913,9 @@ importers:
apps/website:
dependencies:
'@astrojs/sitemap':
specifier: 'catalog:'
version: 3.7.1
'@comfyorg/design-system':
specifier: workspace:*
version: link:../../packages/design-system
@@ -1095,6 +1101,9 @@ packages:
resolution: {integrity: sha512-q8VwfU/fDZNoDOf+r7jUnMC2//H2l0TuQ6FkGJL8vD8nw/q5KiL3DS1KKBI3QhI9UQhpJ5dc7AtqfbXWuOgLCQ==}
engines: {node: 18.20.8 || ^20.3.0 || >=22.0.0}
'@astrojs/sitemap@3.7.1':
resolution: {integrity: sha512-IzQqdTeskaMX+QDZCzMuJIp8A8C1vgzMBp/NmHNnadepHYNHcxQdGLQZYfkbd2EbRXUfOS+UDIKx8sKg0oWVdw==}
'@astrojs/telemetry@3.3.0':
resolution: {integrity: sha512-UFBgfeldP06qu6khs/yY+q1cDAaArM2/7AEIqQ9Cuvf7B1hNLq0xDrZkct+QoIGyjq56y8IaE2I3CTvG99mlhQ==}
engines: {node: 18.20.8 || ^20.3.0 || >=22.0.0}
@@ -4394,6 +4403,9 @@ packages:
'@types/react@19.1.9':
resolution: {integrity: sha512-WmdoynAX8Stew/36uTSVMcLJJ1KRh6L3IZRx1PZ7qJtBqT3dYTgyDTx8H1qoRghErydW7xw9mSJ3wS//tCRpFA==}
'@types/sax@1.2.7':
resolution: {integrity: sha512-rO73L89PJxeYM3s3pPPjiPgVVcymqU490g0YO5n5By0k2Erzj6tay/4lr1CHAAU4JyOWd1rpQ8bCf6cZfHU96A==}
'@types/semver@7.7.0':
resolution: {integrity: sha512-k107IF4+Xr7UHjwDc7Cfd6PRQfbdkiRabXGRjo07b4WyPahFBZCZ1sE+BNxYIJPPg73UkfOsVOLwqVc/6ETrIA==}
@@ -5091,6 +5103,9 @@ packages:
resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==}
engines: {node: '>= 8'}
arg@5.0.2:
resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==}
argparse@1.0.10:
resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==}
@@ -8698,6 +8713,11 @@ packages:
sisteransi@1.0.5:
resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==}
sitemap@9.0.1:
resolution: {integrity: sha512-S6hzjGJSG3d6if0YoF5kTyeRJvia6FSTBroE5fQ0bu1QNxyJqhhinfUsXi9fH3MgtXODWvwo2BDyQSnhPQ88uQ==}
engines: {node: '>=20.19.5', npm: '>=10.8.2'}
hasBin: true
slash@3.0.0:
resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==}
engines: {node: '>=8'}
@@ -8779,6 +8799,9 @@ packages:
prettier:
optional: true
stream-replace-string@2.0.0:
resolution: {integrity: sha512-TlnjJ1C0QrmxRNrON00JvaFFlNh5TTG00APw23j74ET7gkQpTASi6/L2fuiav8pzK715HXtUeClpBTw2NPSn6w==}
string-argv@0.3.2:
resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==}
engines: {node: '>=0.6.19'}
@@ -10090,6 +10113,12 @@ snapshots:
dependencies:
prismjs: 1.30.0
'@astrojs/sitemap@3.7.1':
dependencies:
sitemap: 9.0.1
stream-replace-string: 2.0.0
zod: 4.3.6
'@astrojs/telemetry@3.3.0':
dependencies:
ci-info: 4.4.0
@@ -13442,6 +13471,10 @@ snapshots:
dependencies:
csstype: 3.2.3
'@types/sax@1.2.7':
dependencies:
'@types/node': 25.0.3
'@types/semver@7.7.0': {}
'@types/stats.js@0.17.3': {}
@@ -14248,6 +14281,8 @@ snapshots:
normalize-path: 3.0.0
picomatch: 2.3.1
arg@5.0.2: {}
argparse@1.0.10:
dependencies:
sprintf-js: 1.0.3
@@ -18814,6 +18849,13 @@ snapshots:
sisteransi@1.0.5: {}
sitemap@9.0.1:
dependencies:
'@types/node': 24.10.4
'@types/sax': 1.2.7
arg: 5.0.2
sax: 1.4.4
slash@3.0.0: {}
slice-ansi@4.0.0:
@@ -18902,6 +18944,8 @@ snapshots:
- react-dom
- utf-8-validate
stream-replace-string@2.0.0: {}
string-argv@0.3.2: {}
string-width@4.2.3:

View File

@@ -4,6 +4,7 @@ packages:
catalog:
'@alloc/quick-lru': ^5.2.0
'@astrojs/sitemap': ^3.7.1
'@astrojs/vue': ^5.0.0
'@comfyorg/comfyui-electron-types': 0.6.2
'@eslint/js': ^9.39.1