From d0a3e8c3997ff52ad7124a38520580b02e9113fd Mon Sep 17 00:00:00 2001 From: jwinterm Date: Tue, 17 Mar 2026 13:17:59 -0400 Subject: [PATCH] =?UTF-8?q?fix:=20content=20audit=20=E2=80=94=20SEO,=20cro?= =?UTF-8?q?ss-links,=20draft=20filtering,=20styling?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add sitemap.xml and robots.txt (such.software-site) - Fix blog date on neroswap post (2025→2026) - Filter draft posts from generateStaticParams and getPost - Fix stale product list in website-relaunch blog post - Fix Godot version (4.6→4) - Add cross-links: product pages→blog posts, blog posts→product pages - Add OG/Twitter metadata to blog [slug] pages - Fix Bauhaus Echo privacy: hover color (emerald→blue), contact URL - Fix Suchoice: card styling (p-6→glass-card), font-semibold→font-bold - Fix products listing meta to include Suchoice --- apps/web/content/blog/bauhaus-echo-launch.mdx | 2 ++ apps/web/content/blog/neroswap-aggregator.mdx | 4 ++- .../web/content/blog/smirk-wallet-release.mdx | 2 ++ .../content/blog/website-relaunch-2026.mdx | 2 +- .../blog/wownero-moon-launch-release.mdx | 2 +- apps/web/src/app/blog/[slug]/page.tsx | 12 +++++++++ .../src/app/products/bauhaus-echo/page.tsx | 4 +++ .../products/bauhaus-echo/privacy/page.tsx | 6 ++--- apps/web/src/app/products/page.tsx | 2 +- .../src/app/products/smirk-wallet/page.tsx | 3 +++ apps/web/src/app/products/suchoice/page.tsx | 8 +++--- apps/web/src/app/robots.ts | 8 ++++++ apps/web/src/app/sitemap.ts | 26 +++++++++++++++++++ apps/web/src/lib/blog.ts | 6 +++++ 14 files changed, 76 insertions(+), 11 deletions(-) create mode 100644 apps/web/src/app/robots.ts create mode 100644 apps/web/src/app/sitemap.ts diff --git a/apps/web/content/blog/bauhaus-echo-launch.mdx b/apps/web/content/blog/bauhaus-echo-launch.mdx index 88c44c8..9ac201c 100644 --- a/apps/web/content/blog/bauhaus-echo-launch.mdx +++ b/apps/web/content/blog/bauhaus-echo-launch.mdx @@ -32,3 +32,5 @@ Built with [libGDX](https://libgdx.com/), a cross-platform Java game framework. - **Mobile (free with ads)** — [App Store](https://apps.apple.com/us/app/bauhaus-echo/id6759010657) | [Google Play](https://play.google.com/store/apps/details?id=com.suchsoftware.bauhausecho) - **Desktop ($2, no ads)** — [itch.io](https://suchsoftware.itch.io/bauhaus-echo) - **Web (free)** — [bauhaus.such.software](https://bauhaus.such.software) + +See the full [Bauhaus Echo product page](/products/bauhaus-echo) for more details. diff --git a/apps/web/content/blog/neroswap-aggregator.mdx b/apps/web/content/blog/neroswap-aggregator.mdx index 3184cb2..d89137d 100644 --- a/apps/web/content/blog/neroswap-aggregator.mdx +++ b/apps/web/content/blog/neroswap-aggregator.mdx @@ -1,6 +1,6 @@ --- title: "Neroswap: a Privacy-First DEX & CEX Aggregator" -date: "2025-06-28" +date: "2026-03-17" excerpt: "How we built a real-time orderbook aggregator across 6 decentralized and centralized exchanges — with no KYC, no custody, and no tracking." --- @@ -57,3 +57,5 @@ All the DEX sources (BasicSwap, Eigenwallet) are fully decentralized with on-cha Visit [neroswap.com](https://neroswap.com) to see the live dashboard. The project is built and maintained by [Such Software](/#contact). If you need a custom aggregator, scraping pipeline, or privacy-first web application, [get in touch](/#contact). + +See the full [Neroswap product page](/products/neroswap) for the complete feature list and exchange descriptions. diff --git a/apps/web/content/blog/smirk-wallet-release.mdx b/apps/web/content/blog/smirk-wallet-release.mdx index e0faaea..f7aaf0d 100644 --- a/apps/web/content/blog/smirk-wallet-release.mdx +++ b/apps/web/content/blog/smirk-wallet-release.mdx @@ -29,6 +29,8 @@ Your private spend keys are generated locally and never leave your browser. They Our initial submission was rejected for requesting an unused permission (activeTab). We had included it for a planned feature that was not yet implemented. The fix is already in v0.1.6. We have removed the permission and resubmitted. +See the full [Smirk Wallet product page](/products/smirk-wallet) for more details. + In the meantime, you can install manually from [GitHub Releases](https://github.com/Such-Software/smirk-extension/releases). Firefox support is also available. ## Open Source diff --git a/apps/web/content/blog/website-relaunch-2026.mdx b/apps/web/content/blog/website-relaunch-2026.mdx index 73a12d8..5596a19 100644 --- a/apps/web/content/blog/website-relaunch-2026.mdx +++ b/apps/web/content/blog/website-relaunch-2026.mdx @@ -31,4 +31,4 @@ The NebulaField component is lazy loaded with next/dynamic and requestIdleCallba ## Structure -The site now has Products, Services, Blog, and About sections. Products showcases our standalone offerings: Webshops, Custom Websites, Smirk Wallet, and Bauhaus Echo. Services covers our partnership model and consulting work. Mobile users get a bottom navigation bar. +The site now has Products, Services, Blog, and About sections. Products showcases our standalone offerings: Neroswap, Webshops, Custom Websites, Smirk Wallet, Bauhaus Echo, Suchoice, and Wownero Moon Launch. Services covers our partnership model and consulting work. Mobile users get a bottom navigation bar. diff --git a/apps/web/content/blog/wownero-moon-launch-release.mdx b/apps/web/content/blog/wownero-moon-launch-release.mdx index 6dbc3da..5fef105 100644 --- a/apps/web/content/blog/wownero-moon-launch-release.mdx +++ b/apps/web/content/blog/wownero-moon-launch-release.mdx @@ -41,7 +41,7 @@ Between levels, you spend Moonrocks (earned by collecting crypto pickups) in the ## Tech Stack -Built with [Godot 4.6](https://godotengine.org/) in pure GDScript. The backend (leaderboards, cloud save) runs on our own infrastructure at api.such.software. Ads use AdMob on mobile and AdSense on web, with a "Remove Ads" purchase option. Desktop builds are always ad-free. +Built with [Godot 4](https://godotengine.org/) in pure GDScript. The backend (leaderboards, cloud save) runs on our own infrastructure at api.such.software. Ads use AdMob on mobile and AdSense on web, with a "Remove Ads" purchase option. Desktop builds are always ad-free. ## Get It diff --git a/apps/web/src/app/blog/[slug]/page.tsx b/apps/web/src/app/blog/[slug]/page.tsx index 6f822f1..aee7fdb 100644 --- a/apps/web/src/app/blog/[slug]/page.tsx +++ b/apps/web/src/app/blog/[slug]/page.tsx @@ -20,6 +20,18 @@ export async function generateMetadata({ params }: Props): Promise { title: `${post.title} | Such Software Blog`, description: post.excerpt, alternates: { canonical: `/blog/${slug}` }, + openGraph: { + title: post.title, + description: post.excerpt, + type: "article", + url: `/blog/${slug}`, + siteName: "Such Software", + }, + twitter: { + card: "summary", + title: post.title, + description: post.excerpt, + }, }; } diff --git a/apps/web/src/app/products/bauhaus-echo/page.tsx b/apps/web/src/app/products/bauhaus-echo/page.tsx index 898e3f1..59b56ef 100644 --- a/apps/web/src/app/products/bauhaus-echo/page.tsx +++ b/apps/web/src/app/products/bauhaus-echo/page.tsx @@ -100,6 +100,10 @@ export default function BauhausEchoPage() { Privacy Policy • + + Blog Post + + • Support & Feedback diff --git a/apps/web/src/app/products/bauhaus-echo/privacy/page.tsx b/apps/web/src/app/products/bauhaus-echo/privacy/page.tsx index be58f3b..78c20d2 100644 --- a/apps/web/src/app/products/bauhaus-echo/privacy/page.tsx +++ b/apps/web/src/app/products/bauhaus-echo/privacy/page.tsx @@ -14,7 +14,7 @@ export default function BauhausEchoPrivacyPage() {
- + ← Back to Bauhaus Echo @@ -253,8 +253,8 @@ export default function BauhausEchoPrivacyPage() { Such Software LLC
Email: apps@such.software
Website:{" "} - - https://bauhaus.such.software + + https://such.software

diff --git a/apps/web/src/app/products/page.tsx b/apps/web/src/app/products/page.tsx index 827f730..1eb28d3 100644 --- a/apps/web/src/app/products/page.tsx +++ b/apps/web/src/app/products/page.tsx @@ -6,7 +6,7 @@ import type { Metadata } from "next"; export const metadata: Metadata = { title: "Products | Such Software", - description: "Tools and applications built by Such Software. Neroswap, Webshops, custom websites, Smirk Wallet, Bauhaus Echo, and Wownero Moon Launch.", + description: "Tools and applications built by Such Software. Neroswap, Webshops, Custom Websites, Smirk Wallet, Bauhaus Echo, Suchoice, and Wownero Moon Launch.", alternates: { canonical: "/products" }, openGraph: { title: "Products | Such Software", diff --git a/apps/web/src/app/products/smirk-wallet/page.tsx b/apps/web/src/app/products/smirk-wallet/page.tsx index c69bbff..273ded4 100644 --- a/apps/web/src/app/products/smirk-wallet/page.tsx +++ b/apps/web/src/app/products/smirk-wallet/page.tsx @@ -112,6 +112,9 @@ export default function SmirkWalletPage() {
+ + + diff --git a/apps/web/src/app/products/suchoice/page.tsx b/apps/web/src/app/products/suchoice/page.tsx index 0249e24..e81ddbe 100644 --- a/apps/web/src/app/products/suchoice/page.tsx +++ b/apps/web/src/app/products/suchoice/page.tsx @@ -46,18 +46,18 @@ export default function SuchoicePage() {
-

Features

+

Features

{features.map((feature) => ( - -

{feature.name}

+ +

{feature.name}

{feature.description}

))}
-

Legal

+

Legal

  • diff --git a/apps/web/src/app/robots.ts b/apps/web/src/app/robots.ts new file mode 100644 index 0000000..702d8c9 --- /dev/null +++ b/apps/web/src/app/robots.ts @@ -0,0 +1,8 @@ +import type { MetadataRoute } from "next"; + +export default function robots(): MetadataRoute.Robots { + return { + rules: { userAgent: "*", allow: "/" }, + sitemap: "https://such.software/sitemap.xml", + }; +} diff --git a/apps/web/src/app/sitemap.ts b/apps/web/src/app/sitemap.ts new file mode 100644 index 0000000..421145d --- /dev/null +++ b/apps/web/src/app/sitemap.ts @@ -0,0 +1,26 @@ +import type { MetadataRoute } from "next"; + +export default function sitemap(): MetadataRoute.Sitemap { + const base = "https://such.software"; + return [ + { url: base, lastModified: new Date(), changeFrequency: "weekly", priority: 1.0 }, + { url: `${base}/products`, lastModified: new Date(), changeFrequency: "weekly", priority: 0.9 }, + { url: `${base}/products/neroswap`, lastModified: new Date(), changeFrequency: "monthly", priority: 0.8 }, + { url: `${base}/products/webshops`, lastModified: new Date(), changeFrequency: "monthly", priority: 0.8 }, + { url: `${base}/products/custom-websites`, lastModified: new Date(), changeFrequency: "monthly", priority: 0.8 }, + { url: `${base}/products/smirk-wallet`, lastModified: new Date(), changeFrequency: "monthly", priority: 0.8 }, + { url: `${base}/products/bauhaus-echo`, lastModified: new Date(), changeFrequency: "monthly", priority: 0.8 }, + { url: `${base}/products/suchoice`, lastModified: new Date(), changeFrequency: "monthly", priority: 0.7 }, + { url: `${base}/products/wownero-moon-launch`, lastModified: new Date(), changeFrequency: "monthly", priority: 0.7 }, + { url: `${base}/services`, lastModified: new Date(), changeFrequency: "monthly", priority: 0.9 }, + { url: `${base}/about`, lastModified: new Date(), changeFrequency: "monthly", priority: 0.7 }, + { url: `${base}/support`, lastModified: new Date(), changeFrequency: "monthly", priority: 0.5 }, + { url: `${base}/blog`, lastModified: new Date(), changeFrequency: "weekly", priority: 0.8 }, + { url: `${base}/blog/neroswap-aggregator`, lastModified: new Date(), changeFrequency: "monthly", priority: 0.7 }, + { url: `${base}/blog/crypto-checkout-infrastructure`, lastModified: new Date(), changeFrequency: "monthly", priority: 0.7 }, + { url: `${base}/blog/bauhaus-echo-launch`, lastModified: new Date(), changeFrequency: "monthly", priority: 0.6 }, + { url: `${base}/blog/smirk-wallet-release`, lastModified: new Date(), changeFrequency: "monthly", priority: 0.6 }, + { url: `${base}/blog/website-relaunch-2026`, lastModified: new Date(), changeFrequency: "monthly", priority: 0.5 }, + { url: `${base}/blog/wownero-moon-launch-release`, lastModified: new Date(), changeFrequency: "monthly", priority: 0.6 }, + ]; +} diff --git a/apps/web/src/lib/blog.ts b/apps/web/src/lib/blog.ts index da653aa..debdb9f 100644 --- a/apps/web/src/lib/blog.ts +++ b/apps/web/src/lib/blog.ts @@ -47,6 +47,7 @@ export function getPost(slug: string): Post | null { const raw = fs.readFileSync(filePath, "utf-8"); const { data, content } = matter(raw); + if (data.draft === true) return null; const iso = data.date ?? ""; return { slug, @@ -65,5 +66,10 @@ export function getAllSlugs(): string[] { return fs .readdirSync(CONTENT_DIR) .filter((f) => f.endsWith(".mdx")) + .filter((f) => { + const raw = fs.readFileSync(path.join(CONTENT_DIR, f), "utf-8"); + const { data } = matter(raw); + return data.draft !== true; + }) .map((f) => f.replace(/\.mdx$/, "")); }