Openstatus www.openstatus.dev

๐ŸŒ seo (#1309)

* ๐ŸŒ seo

* ci: apply automated fixes

* ๐Ÿ”‘ fixed deps

* ๐ŸŒ seo to the moon

* ci: apply automated fixes

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>

authored by

Thibault Le Ouay
autofix-ci[bot]
and committed by
GitHub
22218123 2ee5fb84

+178 -37
+1
apps/web/package.json
··· 82 82 "remark-parse": "10.0.2", 83 83 "remark-rehype": "10.1.0", 84 84 "resend": "4.0.1", 85 + "schema-dts": "1.1.5", 85 86 "shiki": "0.14.4", 86 87 "sonner": "1.7.1", 87 88 "stripe": "13.8.0",
+25 -4
apps/web/src/app/(pages)/(content)/(landing)/[slug]/page.tsx
··· 8 8 import type { Metadata } from "next"; 9 9 import Link from "next/link"; 10 10 import { notFound } from "next/navigation"; 11 + import type { WebPage, WithContext } from "schema-dts"; 11 12 12 13 export async function generateStaticParams() { 13 14 return Object.keys(landingsConfig).map((slug) => ({ slug })); ··· 60 61 notFound(); 61 62 } 62 63 64 + const jsonLDWebPage: WithContext<WebPage> = { 65 + "@context": "https://schema.org", 66 + "@type": "WebPage", 67 + name: `${landing.title} | openstatus`, 68 + headline: landing.description, 69 + mainEntityOfPage: { 70 + "@type": "WebPage", 71 + "@id": "https://www.openstatus.dev", 72 + }, 73 + }; 74 + 63 75 return ( 64 - <div className="grid gap-12"> 65 - <Hero hero={landing.hero} description={landing.description} /> 66 - {landing.blocks.map((block) => block)} 67 - </div> 76 + <> 77 + <script 78 + type="application/ld+json" 79 + // biome-ignore lint/security/noDangerouslySetInnerHtml: jsonLd 80 + dangerouslySetInnerHTML={{ 81 + __html: JSON.stringify(jsonLDWebPage).replace(/</g, "\\u003c"), 82 + }} 83 + /> 84 + <div className="grid gap-12"> 85 + <Hero hero={landing.hero} description={landing.description} /> 86 + {landing.blocks.map((block) => block)} 87 + </div> 88 + </> 68 89 ); 69 90 } 70 91
+25
apps/web/src/app/(pages)/(content)/blog/[slug]/page.tsx
··· 18 18 BreadcrumbSeparator, 19 19 } from "@openstatus/ui"; 20 20 import Link from "next/link"; 21 + import type { BlogPosting, WithContext } from "schema-dts"; 21 22 22 23 // export const dynamic = "force-static"; 23 24 ··· 77 78 78 79 if (!post) notFound(); 79 80 81 + const jsonLDBlog: WithContext<BlogPosting> = { 82 + "@context": "https://schema.org", 83 + "@type": "BlogPosting", 84 + name: `${post.title} | openstatus`, 85 + headline: post.title, 86 + datePublished: post.publishedAt.toISOString(), 87 + author: { 88 + "@type": "Person", 89 + name: post.author.name, 90 + url: post.author.url, 91 + }, 92 + mainEntityOfPage: { 93 + "@type": "WebPage", 94 + "@id": "https://www.openstatus.dev", 95 + }, 96 + }; 97 + 80 98 return ( 81 99 <> 100 + <script 101 + type="application/ld+json" 102 + // biome-ignore lint/security/noDangerouslySetInnerHtml: jsonLd 103 + dangerouslySetInnerHTML={{ 104 + __html: JSON.stringify(jsonLDBlog).replace(/</g, "\\u003c"), 105 + }} 106 + /> 82 107 <Breadcrumb className="mb-4 px-3 md:px-6"> 83 108 <BreadcrumbList> 84 109 <BreadcrumbItem>
+88 -12
apps/web/src/app/(pages)/(content)/page.tsx
··· 7 7 import { Partners } from "@/components/marketing/partners"; 8 8 import { Stats } from "@/components/marketing/stats"; 9 9 import { StatusPageCard } from "@/components/marketing/status-page/card"; 10 + import { allPlans } from "@openstatus/db/src/schema/plan/config"; 11 + import type { Organization, Product, WebPage, WithContext } from "schema-dts"; 10 12 11 13 export const revalidate = 600; 12 14 15 + const jsonLdProduct: WithContext<Product> = { 16 + "@context": "https://schema.org", 17 + "@type": "Product", 18 + name: "openstatus", 19 + description: "Opensource uptime and synthetic monitoring.", 20 + brand: { 21 + "@type": "Brand", 22 + name: "openstatus", 23 + logo: "https://openstatus.dev/assets/logos/OpenStatus-Logo.svg", 24 + }, 25 + offers: Object.entries(allPlans).map(([_, value]) => ({ 26 + "@type": "Offer", 27 + price: value.price, 28 + name: value.title, 29 + priceCurrency: "EUR", 30 + availability: "https://schema.org/InStock", 31 + })), 32 + }; 33 + 34 + const jsonLdOrganization: WithContext<Organization> = { 35 + "@context": "https://schema.org", 36 + "@type": "Organization", 37 + name: "openstatus", 38 + url: "https://openstatus.dev", 39 + logo: "https://openstatus.dev/assets/logos/OpenStatus-Logo.svg", 40 + sameAs: [ 41 + "https://github.com/openstatushq", 42 + "https://linkedin.com/company/openstatus", 43 + "https://bsky.app/profile/openstatus.dev", 44 + "https://x.com/openstatushq", 45 + ], 46 + contactPoint: [ 47 + { 48 + "@type": "ContactPoint", 49 + contactType: "Support", 50 + email: "ping@openstatus.dev", 51 + }, 52 + ], 53 + }; 54 + 55 + const jsonLDWebpage: WithContext<WebPage> = { 56 + "@context": "https://schema.org", 57 + "@type": "WebPage", 58 + name: "openstatus", 59 + description: "Opensource uptime and synthetic monitoring.", 60 + url: "https://openstatus.dev", 61 + image: "https://openstatus.dev/assets/logos/OpenStatus-Logo.svg", 62 + headline: "Showcase your uptime with a status page", 63 + }; 64 + 13 65 export default async function LandingPage() { 14 66 return ( 15 - <div className="grid gap-12"> 16 - <Hero /> 17 - <Partners /> 18 - <MonitoringCard /> 19 - <Stats /> 20 - <MiddleCTA /> 21 - <StatusPageCard /> 22 - <AlertCard /> 23 - <CLICard /> 24 - <BottomCTA /> 25 - <LatestChangelogs /> 26 - </div> 67 + <> 68 + <div className="grid gap-12"> 69 + <Hero /> 70 + <Partners /> 71 + <MonitoringCard /> 72 + <Stats /> 73 + <MiddleCTA /> 74 + <StatusPageCard /> 75 + <AlertCard /> 76 + <CLICard /> 77 + <BottomCTA /> 78 + <LatestChangelogs /> 79 + </div> 80 + 81 + <script 82 + type="application/ld+json" 83 + // biome-ignore lint/security/noDangerouslySetInnerHtml: jsonLd 84 + dangerouslySetInnerHTML={{ 85 + __html: JSON.stringify(jsonLdProduct).replace(/</g, "\\u003c"), 86 + }} 87 + /> 88 + <script 89 + type="application/ld+json" 90 + // biome-ignore lint/security/noDangerouslySetInnerHtml: jsonLd 91 + dangerouslySetInnerHTML={{ 92 + __html: JSON.stringify(jsonLdOrganization).replace(/</g, "\\u003c"), 93 + }} 94 + /> 95 + <script 96 + type="application/ld+json" 97 + // biome-ignore lint/security/noDangerouslySetInnerHtml: jsonLd 98 + dangerouslySetInnerHTML={{ 99 + __html: JSON.stringify(jsonLDWebpage).replace(/</g, "\\u003c"), 100 + }} 101 + /> 102 + </> 27 103 ); 28 104 }
+2 -2
apps/web/src/app/api/og/route.tsx
··· 1 1 /* eslint-disable @next/next/no-img-element */ 2 2 import { ImageResponse } from "next/og"; 3 3 4 - import { DESCRIPTION, TITLE } from "@/app/shared-metadata"; 4 + import { OG_DESCRIPTION, TITLE } from "@/app/shared-metadata"; 5 5 import { Background } from "./_components/background"; 6 6 import { 7 7 DEFAULT_URL, ··· 30 30 31 31 const description = 32 32 (searchParams.has("description") && searchParams.get("description")) || 33 - DESCRIPTION; 33 + OG_DESCRIPTION; 34 34 35 35 const image = 36 36 (searchParams.has("image") && searchParams.get("image")) || IMAGE;
+5 -2
apps/web/src/app/shared-metadata.ts
··· 1 1 import type { Metadata } from "next"; 2 2 3 - export const TITLE = "OpenStatus"; 3 + export const TITLE = "openstatus"; 4 4 export const DESCRIPTION = 5 - "A better way to monitor your API and your website. Don't let downtime or a slow response time ruin your user experience. Try the open-source synthetic monitoring platform for free!"; 5 + "Monitor your API and your website globally. Don't let downtime or a slow response time ruin your user experience. Try the open-source uptime and synthetic monitoring platform for free!"; 6 + 7 + export const OG_DESCRIPTION = 8 + "Open-source status page and uptime monitoring system"; 6 9 7 10 export const defaultMetadata: Metadata = { 8 11 title: {
+32 -17
pnpm-lock.yaml
··· 387 387 version: 2.6.2 388 388 drizzle-orm: 389 389 specifier: 0.35.3 390 - version: 0.35.3(@cloudflare/workers-types@4.20250303.0)(@libsql/client-wasm@0.14.0)(@libsql/client@0.14.0(bufferutil@4.0.8)(utf-8-validate@6.0.5))(@opentelemetry/api@1.9.0)(@types/pg@8.11.10)(@types/react@19.1.8)(better-sqlite3@11.7.0)(bun-types@1.2.17)(react@19.1.0) 390 + version: 0.35.3(@cloudflare/workers-types@4.20250303.0)(@libsql/client-wasm@0.14.0)(@libsql/client@0.14.0(bufferutil@4.0.8)(utf-8-validate@6.0.5))(@opentelemetry/api@1.9.0)(@types/pg@8.11.10)(@types/react@19.1.8)(better-sqlite3@11.7.0)(bun-types@1.2.20(@types/react@19.1.8))(react@19.1.0) 391 391 hono: 392 392 specifier: 4.5.3 393 393 version: 4.5.3 ··· 699 699 resend: 700 700 specifier: 4.0.1 701 701 version: 4.0.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0) 702 + schema-dts: 703 + specifier: 1.1.5 704 + version: 1.1.5 702 705 shiki: 703 706 specifier: 0.14.4 704 707 version: 0.14.4 ··· 829 832 version: link:../../packages/tsconfig 830 833 '@types/bun': 831 834 specifier: latest 832 - version: 1.2.17 835 + version: 1.2.20(@types/react@19.1.8) 833 836 typescript: 834 837 specifier: 5.7.2 835 838 version: 5.7.2 ··· 949 952 version: 0.7.1(typescript@5.7.2)(zod@3.24.1) 950 953 drizzle-orm: 951 954 specifier: 0.35.3 952 - version: 0.35.3(@cloudflare/workers-types@4.20250303.0)(@libsql/client-wasm@0.14.0)(@libsql/client@0.14.0(bufferutil@4.0.8)(utf-8-validate@6.0.3))(@opentelemetry/api@1.9.0)(@types/pg@8.11.10)(@types/react@19.1.8)(better-sqlite3@11.4.0)(bun-types@1.2.17)(react@19.1.0) 955 + version: 0.35.3(@cloudflare/workers-types@4.20250303.0)(@libsql/client-wasm@0.14.0)(@libsql/client@0.14.0(bufferutil@4.0.8)(utf-8-validate@6.0.3))(@opentelemetry/api@1.9.0)(@types/pg@8.11.10)(@types/react@19.1.8)(better-sqlite3@11.4.0)(bun-types@1.2.20(@types/react@19.1.8))(react@19.1.0) 953 956 drizzle-zod: 954 957 specifier: 0.5.1 955 - version: 0.5.1(drizzle-orm@0.35.3(@cloudflare/workers-types@4.20250303.0)(@libsql/client-wasm@0.14.0)(@libsql/client@0.14.0(bufferutil@4.0.8)(utf-8-validate@6.0.3))(@opentelemetry/api@1.9.0)(@types/pg@8.11.10)(@types/react@19.1.8)(better-sqlite3@11.4.0)(bun-types@1.2.17)(react@19.1.0))(zod@3.24.1) 958 + version: 0.5.1(drizzle-orm@0.35.3(@cloudflare/workers-types@4.20250303.0)(@libsql/client-wasm@0.14.0)(@libsql/client@0.14.0(bufferutil@4.0.8)(utf-8-validate@6.0.3))(@opentelemetry/api@1.9.0)(@types/pg@8.11.10)(@types/react@19.1.8)(better-sqlite3@11.4.0)(bun-types@1.2.20(@types/react@19.1.8))(react@19.1.0))(zod@3.24.1) 956 959 zod: 957 960 specifier: 3.24.1 958 961 version: 3.24.1 ··· 6401 6404 '@types/braces@3.0.5': 6402 6405 resolution: {integrity: sha512-SQFof9H+LXeWNz8wDe7oN5zu7ket0qwMu5vZubW4GCJ8Kkeh6nBWUz87+KTz/G3Kqsrp0j/W253XJb3KMEeg3w==} 6403 6406 6404 - '@types/bun@1.2.17': 6405 - resolution: {integrity: sha512-l/BYs/JYt+cXA/0+wUhulYJB6a6p//GTPiJ7nV+QHa8iiId4HZmnu/3J/SowP5g0rTiERY2kfGKXEK5Ehltx4Q==} 6407 + '@types/bun@1.2.20': 6408 + resolution: {integrity: sha512-dX3RGzQ8+KgmMw7CsW4xT5ITBSCrSbfHc36SNT31EOUg/LA9JWq0VDdEXDRSe1InVWpd2yLUM1FUF/kEOyTzYA==} 6406 6409 6407 6410 '@types/caseless@0.12.4': 6408 6411 resolution: {integrity: sha512-2in/lrHRNmDvHPgyormtEralhPcN3An1gLjJzj2Bw145VBxkQ75JEXW6CTdMAwShiHQcYsl2d10IjQSdJSJz4g==} ··· 7080 7083 bun-types@1.0.8: 7081 7084 resolution: {integrity: sha512-2dNB+dBwAcFW7RSd4y5vKycRjouKVklSwPk4EjBKWvcMYUBOqZGGNzV7+b2tfKBG3BeRXnozbnegVKR1azuATg==} 7082 7085 7083 - bun-types@1.2.17: 7084 - resolution: {integrity: sha512-ElC7ItwT3SCQwYZDYoAH+q6KT4Fxjl8DtZ6qDulUFBmXA8YB4xo+l54J9ZJN+k2pphfn9vk7kfubeSd5QfTVJQ==} 7086 + bun-types@1.2.20: 7087 + resolution: {integrity: sha512-pxTnQYOrKvdOwyiyd/7sMt9yFOenN004Y6O4lCcCUoKVej48FS5cvTw9geRaEcB9TsDZaJKAxPTVvi8tFsVuXA==} 7088 + peerDependencies: 7089 + '@types/react': ^19 7085 7090 7086 7091 bundle-require@4.0.2: 7087 7092 resolution: {integrity: sha512-jwzPOChofl67PSTW2SGubV9HBQAhhR2i6nskiOThauo9dzwDUgOWQScFVaJkjEfYX+UXiD+LEx8EblQMc2wIag==} ··· 8903 8908 8904 8909 libsql@0.4.5: 8905 8910 resolution: {integrity: sha512-sorTJV6PNt94Wap27Sai5gtVLIea4Otb2LUiAUyr3p6BPOScGMKGt5F1b5X/XgkNtcsDKeX5qfeBDj+PdShclQ==} 8911 + cpu: [x64, arm64, wasm32] 8906 8912 os: [darwin, linux, win32] 8907 8913 8908 8914 libsql@0.5.13: 8909 8915 resolution: {integrity: sha512-5Bwoa/CqzgkTwySgqHA5TsaUDRrdLIbdM4egdPcaAnqO3aC+qAgS6BwdzuZwARA5digXwiskogZ8H7Yy4XfdOg==} 8916 + cpu: [x64, arm64, wasm32, arm] 8910 8917 os: [darwin, linux, win32] 8911 8918 8912 8919 lightningcss-darwin-arm64@1.30.1: ··· 10785 10792 scheduler@0.26.0: 10786 10793 resolution: {integrity: sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==} 10787 10794 10795 + schema-dts@1.1.5: 10796 + resolution: {integrity: sha512-RJr9EaCmsLzBX2NDiO5Z3ux2BVosNZN5jo0gWgsyKvxKIUL5R3swNvoorulAeL9kLB0iTSX7V6aokhla2m7xbg==} 10797 + 10788 10798 schema-utils@3.3.0: 10789 10799 resolution: {integrity: sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==} 10790 10800 engines: {node: '>= 10.13.0'} ··· 17544 17554 17545 17555 '@types/braces@3.0.5': {} 17546 17556 17547 - '@types/bun@1.2.17': 17557 + '@types/bun@1.2.20(@types/react@19.1.8)': 17548 17558 dependencies: 17549 - bun-types: 1.2.17 17559 + bun-types: 1.2.20(@types/react@19.1.8) 17560 + transitivePeerDependencies: 17561 + - '@types/react' 17550 17562 17551 17563 '@types/caseless@0.12.4': {} 17552 17564 ··· 18438 18450 18439 18451 bun-types@1.0.8: {} 18440 18452 18441 - bun-types@1.2.17: 18453 + bun-types@1.2.20(@types/react@19.1.8): 18442 18454 dependencies: 18443 18455 '@types/node': 24.0.8 18456 + '@types/react': 19.1.8 18444 18457 18445 18458 bundle-require@4.0.2(esbuild@0.18.20): 18446 18459 dependencies: ··· 19003 19016 transitivePeerDependencies: 19004 19017 - supports-color 19005 19018 19006 - drizzle-orm@0.35.3(@cloudflare/workers-types@4.20250303.0)(@libsql/client-wasm@0.14.0)(@libsql/client@0.14.0(bufferutil@4.0.8)(utf-8-validate@6.0.3))(@opentelemetry/api@1.9.0)(@types/pg@8.11.10)(@types/react@19.1.8)(better-sqlite3@11.4.0)(bun-types@1.2.17)(react@19.1.0): 19019 + drizzle-orm@0.35.3(@cloudflare/workers-types@4.20250303.0)(@libsql/client-wasm@0.14.0)(@libsql/client@0.14.0(bufferutil@4.0.8)(utf-8-validate@6.0.3))(@opentelemetry/api@1.9.0)(@types/pg@8.11.10)(@types/react@19.1.8)(better-sqlite3@11.4.0)(bun-types@1.2.20(@types/react@19.1.8))(react@19.1.0): 19007 19020 dependencies: 19008 19021 '@libsql/client-wasm': 0.14.0 19009 19022 optionalDependencies: ··· 19013 19026 '@types/pg': 8.11.10 19014 19027 '@types/react': 19.1.8 19015 19028 better-sqlite3: 11.4.0 19016 - bun-types: 1.2.17 19029 + bun-types: 1.2.20(@types/react@19.1.8) 19017 19030 react: 19.1.0 19018 19031 19019 - drizzle-orm@0.35.3(@cloudflare/workers-types@4.20250303.0)(@libsql/client-wasm@0.14.0)(@libsql/client@0.14.0(bufferutil@4.0.8)(utf-8-validate@6.0.5))(@opentelemetry/api@1.9.0)(@types/pg@8.11.10)(@types/react@19.1.8)(better-sqlite3@11.7.0)(bun-types@1.2.17)(react@19.1.0): 19032 + drizzle-orm@0.35.3(@cloudflare/workers-types@4.20250303.0)(@libsql/client-wasm@0.14.0)(@libsql/client@0.14.0(bufferutil@4.0.8)(utf-8-validate@6.0.5))(@opentelemetry/api@1.9.0)(@types/pg@8.11.10)(@types/react@19.1.8)(better-sqlite3@11.7.0)(bun-types@1.2.20(@types/react@19.1.8))(react@19.1.0): 19020 19033 dependencies: 19021 19034 '@libsql/client-wasm': 0.14.0 19022 19035 optionalDependencies: ··· 19026 19039 '@types/pg': 8.11.10 19027 19040 '@types/react': 19.1.8 19028 19041 better-sqlite3: 11.7.0 19029 - bun-types: 1.2.17 19042 + bun-types: 1.2.20(@types/react@19.1.8) 19030 19043 react: 19.1.0 19031 19044 19032 - drizzle-zod@0.5.1(drizzle-orm@0.35.3(@cloudflare/workers-types@4.20250303.0)(@libsql/client-wasm@0.14.0)(@libsql/client@0.14.0(bufferutil@4.0.8)(utf-8-validate@6.0.3))(@opentelemetry/api@1.9.0)(@types/pg@8.11.10)(@types/react@19.1.8)(better-sqlite3@11.4.0)(bun-types@1.2.17)(react@19.1.0))(zod@3.24.1): 19045 + drizzle-zod@0.5.1(drizzle-orm@0.35.3(@cloudflare/workers-types@4.20250303.0)(@libsql/client-wasm@0.14.0)(@libsql/client@0.14.0(bufferutil@4.0.8)(utf-8-validate@6.0.3))(@opentelemetry/api@1.9.0)(@types/pg@8.11.10)(@types/react@19.1.8)(better-sqlite3@11.4.0)(bun-types@1.2.20(@types/react@19.1.8))(react@19.1.0))(zod@3.24.1): 19033 19046 dependencies: 19034 - drizzle-orm: 0.35.3(@cloudflare/workers-types@4.20250303.0)(@libsql/client-wasm@0.14.0)(@libsql/client@0.14.0(bufferutil@4.0.8)(utf-8-validate@6.0.3))(@opentelemetry/api@1.9.0)(@types/pg@8.11.10)(@types/react@19.1.8)(better-sqlite3@11.4.0)(bun-types@1.2.17)(react@19.1.0) 19047 + drizzle-orm: 0.35.3(@cloudflare/workers-types@4.20250303.0)(@libsql/client-wasm@0.14.0)(@libsql/client@0.14.0(bufferutil@4.0.8)(utf-8-validate@6.0.3))(@opentelemetry/api@1.9.0)(@types/pg@8.11.10)(@types/react@19.1.8)(better-sqlite3@11.4.0)(bun-types@1.2.20(@types/react@19.1.8))(react@19.1.0) 19035 19048 zod: 3.24.1 19036 19049 19037 19050 dset@3.1.4: {} ··· 23060 23073 sax@1.4.1: {} 23061 23074 23062 23075 scheduler@0.26.0: {} 23076 + 23077 + schema-dts@1.1.5: {} 23063 23078 23064 23079 schema-utils@3.3.0: 23065 23080 dependencies: