diff --git a/app/routes.ts b/app/routes.ts
index 0c91f4d..1a7cdb8 100644
--- a/app/routes.ts
+++ b/app/routes.ts
@@ -1,5 +1,7 @@
-import { type RouteConfig, index } from "@react-router/dev/routes";
+import { type RouteConfig, index, route } from "@react-router/dev/routes";
export default [
- index("routes/home.tsx")
+ index("routes/home.tsx"),
+ route("robots.txt", "routes/robots.ts"),
+ route("sitemap.xml", "routes/sitemap.ts"),
] satisfies RouteConfig;
diff --git a/app/routes/home.tsx b/app/routes/home.tsx
index 5a73344..00e4a35 100644
--- a/app/routes/home.tsx
+++ b/app/routes/home.tsx
@@ -2,15 +2,26 @@ import { useEffect, useState } from "react"
import { ArrowDown } from "lucide-react"
import type { Route } from "./+types/home"
+import { homepageJsonLd, site } from "../seo"
export function meta({}: Route.MetaArgs) {
return [
- { title: "BAGAS AULIA REZKI - Portfolio" },
+ { title: site.title },
{
name: "description",
- content:
- "Portfolio of Bagas Aulia Rezki, a Fullstack Developer and Information Systems student.",
+ content: site.description,
},
+ { tagName: "link", rel: "canonical", href: site.url },
+ { name: "robots", content: "index, follow" },
+ { property: "og:type", content: "website" },
+ { property: "og:url", content: site.url },
+ { property: "og:title", content: site.title },
+ { property: "og:description", content: site.description },
+ { property: "og:site_name", content: site.name },
+ { name: "twitter:card", content: "summary_large_image" },
+ { name: "twitter:title", content: site.title },
+ { name: "twitter:description", content: site.description },
+ { "script:ld+json": homepageJsonLd },
]
}
@@ -358,9 +369,9 @@ export default function Home() {
-
+
developer / student / self-hoster
-
+
diff --git a/app/routes/robots.ts b/app/routes/robots.ts
new file mode 100644
index 0000000..a6da3a7
--- /dev/null
+++ b/app/routes/robots.ts
@@ -0,0 +1,17 @@
+import { site } from "../seo"
+
+export function loader() {
+ const body = [
+ "User-agent: *",
+ "Allow: /",
+ "",
+ `Sitemap: ${site.origin}/sitemap.xml`,
+ "",
+ ].join("\n")
+
+ return new Response(body, {
+ headers: {
+ "Content-Type": "text/plain; charset=utf-8",
+ },
+ })
+}
diff --git a/app/routes/sitemap.ts b/app/routes/sitemap.ts
new file mode 100644
index 0000000..49d7a65
--- /dev/null
+++ b/app/routes/sitemap.ts
@@ -0,0 +1,17 @@
+import { site } from "../seo"
+
+export function loader() {
+ const body = `
+
+
+ ${site.url}
+
+
+`
+
+ return new Response(body, {
+ headers: {
+ "Content-Type": "application/xml; charset=utf-8",
+ },
+ })
+}
diff --git a/app/seo.ts b/app/seo.ts
new file mode 100644
index 0000000..c7ccc51
--- /dev/null
+++ b/app/seo.ts
@@ -0,0 +1,38 @@
+export const site = {
+ origin: "https://fossy.my.id",
+ url: "https://fossy.my.id/",
+ name: "Bagas Aulia Rezki",
+ title: "Bagas Aulia Rezki | Backend, DevOps & Full-Stack Developer",
+ description:
+ "Portfolio of Bagas Aulia Rezki, a Full-Stack and Backend Developer building Go services, DevOps infrastructure, automation, and self-hosted systems.",
+} as const
+
+export const homepageJsonLd = {
+ "@context": "https://schema.org",
+ "@graph": [
+ {
+ "@type": "WebSite",
+ "@id": `${site.origin}/#website`,
+ url: site.url,
+ name: site.name,
+ },
+ {
+ "@type": "ProfilePage",
+ "@id": `${site.origin}/#profile`,
+ url: site.url,
+ mainEntity: {
+ "@id": `${site.origin}/#person`,
+ },
+ },
+ {
+ "@type": "Person",
+ "@id": `${site.origin}/#person`,
+ name: site.name,
+ url: site.url,
+ sameAs: [
+ "https://github.com/fossyy",
+ "https://www.linkedin.com/in/bagas-aulia-rezki/",
+ ],
+ },
+ ],
+}