mirror of
https://github.com/Such-Software/such.software.git
synced 2026-10-17 05:32:05 +02:00
fix: content audit — SEO, cross-links, draft filtering, styling
- 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
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -20,6 +20,18 @@ export async function generateMetadata({ params }: Props): Promise<Metadata> {
|
||||
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,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -100,6 +100,10 @@ export default function BauhausEchoPage() {
|
||||
Privacy Policy
|
||||
</Link>
|
||||
<span>•</span>
|
||||
<Link href="/blog/bauhaus-echo-launch" className="hover:text-foreground transition-colors">
|
||||
Blog Post
|
||||
</Link>
|
||||
<span>•</span>
|
||||
<Link href="/support" className="hover:text-foreground transition-colors">
|
||||
Support & Feedback
|
||||
</Link>
|
||||
|
||||
@@ -14,7 +14,7 @@ export default function BauhausEchoPrivacyPage() {
|
||||
<main className="relative min-h-screen flex flex-col items-center bg-background text-foreground">
|
||||
<Header />
|
||||
<div id="main-content" className="z-10 w-full max-w-4xl mx-auto py-20 px-4 pb-24 md:pb-20">
|
||||
<Link href="/products/bauhaus-echo" className="text-sm text-muted-foreground hover:text-emerald-600 dark:hover:text-emerald-400 mb-8 inline-flex items-center gap-2 transition-colors">
|
||||
<Link href="/products/bauhaus-echo" className="text-sm text-muted-foreground hover:text-blue-600 dark:hover:text-blue-400 mb-8 inline-flex items-center gap-2 transition-colors">
|
||||
← Back to Bauhaus Echo
|
||||
</Link>
|
||||
|
||||
@@ -253,8 +253,8 @@ export default function BauhausEchoPrivacyPage() {
|
||||
<strong>Such Software LLC</strong><br />
|
||||
Email: apps@such.software<br />
|
||||
Website:{" "}
|
||||
<a href="https://bauhaus.such.software" target="_blank" rel="noopener noreferrer">
|
||||
https://bauhaus.such.software
|
||||
<a href="https://such.software" target="_blank" rel="noopener noreferrer">
|
||||
https://such.software
|
||||
</a>
|
||||
</p>
|
||||
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -112,6 +112,9 @@ export default function SmirkWalletPage() {
|
||||
</div>
|
||||
|
||||
<div className="flex gap-4">
|
||||
<Link href="/blog/smirk-wallet-release">
|
||||
<Button>Read the Blog Post</Button>
|
||||
</Link>
|
||||
<a href="https://t.me/smirkwallet" target="_blank" rel="noopener noreferrer">
|
||||
<Button variant="outline">Telegram</Button>
|
||||
</a>
|
||||
|
||||
@@ -46,18 +46,18 @@ export default function SuchoicePage() {
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<h2 className="text-2xl font-semibold mb-6">Features</h2>
|
||||
<h2 className="text-2xl font-bold mb-6">Features</h2>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6 mb-12">
|
||||
{features.map((feature) => (
|
||||
<Card key={feature.name} className="p-6">
|
||||
<h3 className="text-xl font-medium mb-2">{feature.name}</h3>
|
||||
<Card key={feature.name} className="glass-card border-pink-500/20">
|
||||
<h3 className="text-xl font-bold mb-2">{feature.name}</h3>
|
||||
<p className="text-muted-foreground">{feature.description}</p>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="mt-16 pt-8 border-t border-border">
|
||||
<h2 className="text-2xl font-semibold mb-4">Legal</h2>
|
||||
<h2 className="text-2xl font-bold mb-4">Legal</h2>
|
||||
<ul className="space-y-2">
|
||||
<li>
|
||||
<Link href="/products/suchoice/privacy" className="text-blue-600 dark:text-blue-400 hover:underline">
|
||||
|
||||
@@ -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",
|
||||
};
|
||||
}
|
||||
@@ -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 },
|
||||
];
|
||||
}
|
||||
@@ -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$/, ""));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user