From 787142cc2843df6e0b71ddf2f88ee30f685aeddd Mon Sep 17 00:00:00 2001 From: Ali Farooq Date: Sun, 12 May 2024 19:01:10 +0500 Subject: [PATCH] feat(saas): Improved SEO, Metadata to pages --- .../src/app/(web)/blog/[...slug]/page.tsx | 20 ++++++- .../app/(web)/blog/_constants/page-config.ts | 3 ++ starterkits/saas/src/app/(web)/blog/page.tsx | 6 +++ .../(web)/changelog/_constants/page-config.ts | 3 ++ .../saas/src/app/(web)/changelog/page.tsx | 13 +++-- starterkits/saas/src/app/(web)/page.tsx | 7 ++- .../(web)/pricing/_constants/page-config.ts | 3 ++ .../saas/src/app/(web)/pricing/page.tsx | 6 +++ .../(web)/support/_constants/page-config.ts | 6 +++ .../saas/src/app/(web)/support/page.tsx | 12 ++++- .../app/auth/login/_constants/page-config.ts | 6 +++ starterkits/saas/src/app/auth/login/page.tsx | 7 +++ .../app/auth/signup/_constants/page-config.ts | 6 +++ starterkits/saas/src/app/auth/signup/page.tsx | 7 +++ .../saas/src/app/docs/[[...slug]]/page.tsx | 24 +++++++-- .../org/[orgId]/_constants/page-config.ts | 5 ++ .../saas/src/app/invite/org/[orgId]/page.tsx | 18 ++++++- starterkits/saas/src/app/layout.tsx | 19 +++++-- .../app/maintenance/_constants/page-config.ts | 5 ++ starterkits/saas/src/app/maintenance/page.tsx | 7 +++ starterkits/saas/src/app/shared-metadata.ts | 39 ++++++++++++++ starterkits/saas/src/app/sitemap.ts | 29 ++++++++++ .../app/waitlist/_constants/page-config.ts | 5 ++ starterkits/saas/src/app/waitlist/page.tsx | 7 +++ starterkits/saas/src/config/site.ts | 6 ++- starterkits/saas/src/config/urls.ts | 20 +++++++ .../src/content/blog/create-saas-in-1-day.mdx | 2 +- .../saas/src/content/blog/introduction.mdx | 3 +- .../src/content/changelogs/version-0.0.0.mdx | 1 - .../src/content/changelogs/version-0.1.0.mdx | 1 - .../src/content/changelogs/version-0.2.4.mdx | 1 - .../saas/src/content/docs/installation.mdx | 3 +- .../saas/src/content/docs/introduction.mdx | 1 + starterkits/saas/src/middleware.ts | 54 ++++++++++--------- .../saas/src/validations/mdx-content.ts | 4 +- 35 files changed, 306 insertions(+), 53 deletions(-) create mode 100644 starterkits/saas/src/app/(web)/blog/_constants/page-config.ts create mode 100644 starterkits/saas/src/app/(web)/changelog/_constants/page-config.ts create mode 100644 starterkits/saas/src/app/(web)/pricing/_constants/page-config.ts create mode 100644 starterkits/saas/src/app/(web)/support/_constants/page-config.ts create mode 100644 starterkits/saas/src/app/auth/login/_constants/page-config.ts create mode 100644 starterkits/saas/src/app/auth/signup/_constants/page-config.ts create mode 100644 starterkits/saas/src/app/invite/org/[orgId]/_constants/page-config.ts create mode 100644 starterkits/saas/src/app/maintenance/_constants/page-config.ts create mode 100644 starterkits/saas/src/app/shared-metadata.ts create mode 100644 starterkits/saas/src/app/sitemap.ts create mode 100644 starterkits/saas/src/app/waitlist/_constants/page-config.ts diff --git a/starterkits/saas/src/app/(web)/blog/[...slug]/page.tsx b/starterkits/saas/src/app/(web)/blog/[...slug]/page.tsx index 8f11473..0c36638 100644 --- a/starterkits/saas/src/app/(web)/blog/[...slug]/page.tsx +++ b/starterkits/saas/src/app/(web)/blog/[...slug]/page.tsx @@ -5,6 +5,7 @@ import { getBlogs } from "@/server/actions/blog"; import { format } from "date-fns"; import Image from "next/image"; import { notFound, redirect } from "next/navigation"; +import { type Metadata } from "next"; export const dynamic = "force-static"; @@ -14,6 +15,23 @@ type BlogSlugPageProps = { }; }; +export async function generateMetadata({ + params, +}: BlogSlugPageProps): Promise { + const slug = `/${params.slug.join("/")}`; + + const blog = (await getBlogs()).find((b) => b.metaData.slug === slug); + + if (!blog) { + return notFound(); + } + + return { + title: blog.metaData.title, + description: blog.metaData.description, + }; +} + export async function generateStaticParams() { const blogs = await getBlogs(); @@ -27,7 +45,7 @@ export default async function BlogSlugPage({ params }: BlogSlugPageProps) { return redirect(siteUrls.blog); } - const slug = params.slug.join("/"); + const slug = `/${params.slug.join("/")}`; const blog = (await getBlogs()).find((b) => b.metaData.slug === slug); diff --git a/starterkits/saas/src/app/(web)/blog/_constants/page-config.ts b/starterkits/saas/src/app/(web)/blog/_constants/page-config.ts new file mode 100644 index 0000000..1bfb27d --- /dev/null +++ b/starterkits/saas/src/app/(web)/blog/_constants/page-config.ts @@ -0,0 +1,3 @@ +export const blogPageConfig = { + title: "Blog", +} as const; diff --git a/starterkits/saas/src/app/(web)/blog/page.tsx b/starterkits/saas/src/app/(web)/blog/page.tsx index cd6794a..ccdb5a5 100644 --- a/starterkits/saas/src/app/(web)/blog/page.tsx +++ b/starterkits/saas/src/app/(web)/blog/page.tsx @@ -8,6 +8,12 @@ import { getBlogs } from "@/server/actions/blog"; import { format } from "date-fns"; import Image from "next/image"; import Link from "next/link"; +import { type Metadata } from "next"; +import { blogPageConfig } from "@/app/(web)/blog/_constants/page-config"; + +export const metadata: Metadata = { + title: blogPageConfig.title, +}; export const dynamic = "force-static"; diff --git a/starterkits/saas/src/app/(web)/changelog/_constants/page-config.ts b/starterkits/saas/src/app/(web)/changelog/_constants/page-config.ts new file mode 100644 index 0000000..044c199 --- /dev/null +++ b/starterkits/saas/src/app/(web)/changelog/_constants/page-config.ts @@ -0,0 +1,3 @@ +export const changelogPageConfig = { + title: "Change Log", +} as const; diff --git a/starterkits/saas/src/app/(web)/changelog/page.tsx b/starterkits/saas/src/app/(web)/changelog/page.tsx index 09386f9..58a2ac2 100644 --- a/starterkits/saas/src/app/(web)/changelog/page.tsx +++ b/starterkits/saas/src/app/(web)/changelog/page.tsx @@ -13,6 +13,12 @@ import { siteConfig } from "@/config/site"; import { getChangelogs } from "@/server/actions/changelog"; import { format } from "date-fns"; import Image from "next/image"; +import type { Metadata } from "next"; +import { changelogPageConfig } from "@/app/(web)/changelog/_constants/page-config"; + +export const metadata: Metadata = { + title: changelogPageConfig.title, +}; export const dynamic = "force-static"; @@ -35,11 +41,8 @@ export default async function ChangeLogPage() {

- {changelogs.map((changelog) => ( - + {changelogs.map((changelog, index) => ( + ))}
diff --git a/starterkits/saas/src/app/(web)/page.tsx b/starterkits/saas/src/app/(web)/page.tsx index 8591bf4..e07ad7b 100644 --- a/starterkits/saas/src/app/(web)/page.tsx +++ b/starterkits/saas/src/app/(web)/page.tsx @@ -11,6 +11,11 @@ import { siteUrls } from "@/config/urls"; import Image from "next/image"; import Link from "next/link"; import Balancer from "react-wrap-balancer"; +import type { Metadata } from "next"; + +export const metadata: Metadata = { + title: "Build Your MVP in Days, not weeks. Next.js Starter Kit", +}; export const dynamic = "force-static"; @@ -19,7 +24,7 @@ export default async function HomePage() { diff --git a/starterkits/saas/src/app/(web)/support/_constants/page-config.ts b/starterkits/saas/src/app/(web)/support/_constants/page-config.ts new file mode 100644 index 0000000..31806e9 --- /dev/null +++ b/starterkits/saas/src/app/(web)/support/_constants/page-config.ts @@ -0,0 +1,6 @@ +import { siteConfig } from "@/config/site"; + +export const supportPageConfig = { + title: "Support", + description: `Get support from ${siteConfig.name} to get started building your next project.`, +} as const; diff --git a/starterkits/saas/src/app/(web)/support/page.tsx b/starterkits/saas/src/app/(web)/support/page.tsx index b5f2c19..3c2225d 100644 --- a/starterkits/saas/src/app/(web)/support/page.tsx +++ b/starterkits/saas/src/app/(web)/support/page.tsx @@ -12,11 +12,21 @@ import { import { type SupportInfo, supportInfos } from "@/config/support"; import { ArrowRightIcon } from "lucide-react"; import Link from "next/link"; +import { type Metadata } from "next"; +import { supportPageConfig } from "@/app/(web)/support/_constants/page-config"; + +export const metadata: Metadata = { + title: supportPageConfig.title, + description: supportPageConfig.description, +}; export default function ContactPage() { return ( - +

If you have any questions or need help, feel free to reach out to us. diff --git a/starterkits/saas/src/app/auth/login/_constants/page-config.ts b/starterkits/saas/src/app/auth/login/_constants/page-config.ts new file mode 100644 index 0000000..d2797b9 --- /dev/null +++ b/starterkits/saas/src/app/auth/login/_constants/page-config.ts @@ -0,0 +1,6 @@ +import { siteConfig } from "@/config/site"; + +export const loginPageConfig = { + title: "Login", + description: `Login to ${siteConfig.name} to get started building your next project.`, +} as const; diff --git a/starterkits/saas/src/app/auth/login/page.tsx b/starterkits/saas/src/app/auth/login/page.tsx index cae58a0..e5d92b1 100644 --- a/starterkits/saas/src/app/auth/login/page.tsx +++ b/starterkits/saas/src/app/auth/login/page.tsx @@ -1,4 +1,11 @@ import { AuthForm } from "@/app/auth/_components/auth-form"; +import { loginPageConfig } from "@/app/auth/login/_constants/page-config"; +import { type Metadata } from "next"; + +export const metadata: Metadata = { + title: loginPageConfig.title, + description: loginPageConfig.description, +}; export default function Login() { return ; diff --git a/starterkits/saas/src/app/auth/signup/_constants/page-config.ts b/starterkits/saas/src/app/auth/signup/_constants/page-config.ts new file mode 100644 index 0000000..ac783bc --- /dev/null +++ b/starterkits/saas/src/app/auth/signup/_constants/page-config.ts @@ -0,0 +1,6 @@ +import { siteConfig } from "@/config/site"; + +export const signupPageConfig = { + title: "Signup", + description: `Signup to ${siteConfig.name} to get started building your next project.`, +} as const; diff --git a/starterkits/saas/src/app/auth/signup/page.tsx b/starterkits/saas/src/app/auth/signup/page.tsx index 7e4f572..327138a 100644 --- a/starterkits/saas/src/app/auth/signup/page.tsx +++ b/starterkits/saas/src/app/auth/signup/page.tsx @@ -1,4 +1,11 @@ import { AuthForm } from "@/app/auth/_components/auth-form"; +import { signupPageConfig } from "@/app/auth/signup/_constants/page-config"; +import { type Metadata } from "next"; + +export const metadata: Metadata = { + title: signupPageConfig.title, + description: signupPageConfig.description, +}; export default function Signup() { return ; diff --git a/starterkits/saas/src/app/docs/[[...slug]]/page.tsx b/starterkits/saas/src/app/docs/[[...slug]]/page.tsx index 20f176d..2046146 100644 --- a/starterkits/saas/src/app/docs/[[...slug]]/page.tsx +++ b/starterkits/saas/src/app/docs/[[...slug]]/page.tsx @@ -1,6 +1,7 @@ import { notFound } from "next/navigation"; import { Toc } from "@/components/toc"; import { getDocs } from "@/server/actions/docs"; +import { type Metadata } from "next"; export const dynamic = "force-static"; @@ -10,20 +11,33 @@ type DocsSlugPageProps = { }; }; +export async function generateMetadata({ + params, +}: DocsSlugPageProps): Promise { + const slug = Array.isArray(params.slug) ? `/${params.slug.join("/")}` : "/"; + + const doc = (await getDocs()).find((doc) => doc.metaData.slug === slug); + + if (!doc) { + return notFound(); + } + + return { + title: doc.metaData.title, + description: doc.metaData.description, + }; +} + export async function generateStaticParams() { const docs = await getDocs(); - docs.map((doc) => { - console.log(doc.metaData.slug.split("/") || ["/"]); - }); - return docs.map((doc) => ({ slug: doc.metaData.slug.split("/") || ["/"], })); } export default async function DocsSlugPage({ params }: DocsSlugPageProps) { - const slug = Array.isArray(params.slug) ? params.slug.join("/") : "/"; + const slug = Array.isArray(params.slug) ? `/${params.slug.join("/")}` : "/"; const doc = (await getDocs()).find((doc) => doc.metaData.slug === slug); diff --git a/starterkits/saas/src/app/invite/org/[orgId]/_constants/page-config.ts b/starterkits/saas/src/app/invite/org/[orgId]/_constants/page-config.ts new file mode 100644 index 0000000..7284ceb --- /dev/null +++ b/starterkits/saas/src/app/invite/org/[orgId]/_constants/page-config.ts @@ -0,0 +1,5 @@ +export const invitePageConfig = { + title: ({ orgName }: { orgName: string }) => `Invite to ${orgName}`, + description: ({ orgName }: { orgName: string }) => + `Invite your team to ${orgName} and get started building your next project.`, +} as const; diff --git a/starterkits/saas/src/app/invite/org/[orgId]/page.tsx b/starterkits/saas/src/app/invite/org/[orgId]/page.tsx index a582e8a..2149e7c 100644 --- a/starterkits/saas/src/app/invite/org/[orgId]/page.tsx +++ b/starterkits/saas/src/app/invite/org/[orgId]/page.tsx @@ -1,8 +1,9 @@ import { getOrgByIdQuery } from "@/server/actions/organization/queries"; import { RequestCard } from "@/app/invite/org/[orgId]/_components/request-card"; import { notFound } from "next/navigation"; +import { type Metadata } from "next"; -type OrgRequestProps = { +export type OrgRequestProps = { params: { orgId: string; }; @@ -23,3 +24,18 @@ export default async function OrgRequestPage({ ); } + +export async function generateMetadata({ + params, +}: OrgRequestProps): Promise { + const org = await getOrgByIdQuery({ orgId: params.orgId }); + + if (!org) { + return notFound(); + } + + return { + title: `Invite to ${org.name}`, + description: `Invite your team to ${org.name} and get started building your next project.`, + }; +} diff --git a/starterkits/saas/src/app/layout.tsx b/starterkits/saas/src/app/layout.tsx index c3ed38e..f98910c 100644 --- a/starterkits/saas/src/app/layout.tsx +++ b/starterkits/saas/src/app/layout.tsx @@ -4,12 +4,21 @@ import { Toaster } from "@/components/ui/sonner"; import "@/styles/globals.css"; import "@/styles/prism.css"; import { fontHeading, fontSans } from "@/lib/fonts"; +import { type Metadata } from "next"; +import { + defaultMetadata, + twitterMetadata, + ogMetadata, +} from "@/app/shared-metadata"; -export const metadata = { - title: "RapidLaunch - Next.js Boilerplate", - description: - "Next.js boilerplate with shadcn ui, TRPC, TailwindCSS, and Drizzle.", - icons: [{ rel: "icon", url: "/favicon.ico" }], +export const metadata: Metadata = { + ...defaultMetadata, + twitter: { + ...twitterMetadata, + }, + openGraph: { + ...ogMetadata, + }, }; export default function RootLayout({ diff --git a/starterkits/saas/src/app/maintenance/_constants/page-config.ts b/starterkits/saas/src/app/maintenance/_constants/page-config.ts new file mode 100644 index 0000000..52aa684 --- /dev/null +++ b/starterkits/saas/src/app/maintenance/_constants/page-config.ts @@ -0,0 +1,5 @@ +export const maintenancePageConfig = { + title: "Maintenance", + description: + "We're currently undergoing maintenance. Please check back later.", +} as const; diff --git a/starterkits/saas/src/app/maintenance/page.tsx b/starterkits/saas/src/app/maintenance/page.tsx index f8f68fd..7cff144 100644 --- a/starterkits/saas/src/app/maintenance/page.tsx +++ b/starterkits/saas/src/app/maintenance/page.tsx @@ -1,4 +1,11 @@ +import { maintenancePageConfig } from "@/app/maintenance/_constants/page-config"; import { siteConfig } from "@/config/site"; +import { type Metadata } from "next"; + +export const metadata: Metadata = { + title: maintenancePageConfig.title, + description: maintenancePageConfig.description, +}; export default function Maintenance() { return ( diff --git a/starterkits/saas/src/app/shared-metadata.ts b/starterkits/saas/src/app/shared-metadata.ts new file mode 100644 index 0000000..63818fe --- /dev/null +++ b/starterkits/saas/src/app/shared-metadata.ts @@ -0,0 +1,39 @@ +import { siteConfig } from "@/config/site"; +import { siteUrls } from "@/config/urls"; +import type { Metadata } from "next"; + +export const defaultMetadata: Metadata = { + title: { + template: `%s | ${siteConfig.name}`, + default: siteConfig.name, + }, + description: siteConfig.description, + metadataBase: new URL(siteUrls.publicUrl), + keywords: [ + "Next.js", + "React", + "Next.js Starter kit", + "SaaS Starter Kit", + "Shadcn UI", + ], + authors: [{ name: "Ali Farooq", url: "https://twitter.com/alifarooqdev" }], + creator: "AliFarooqDev", +}; + +export const twitterMetadata: Metadata["twitter"] = { + title: siteConfig.name, + description: siteConfig.description, + card: "summary_large_image", + images: [siteConfig.orgImage], + creator: "@alifarooqdev", +}; + +export const ogMetadata: Metadata["openGraph"] = { + title: siteConfig.name, + description: siteConfig.description, + type: "website", + images: [{ url: siteConfig.orgImage, alt: siteConfig.name }], + locale: "en_US", + url: siteUrls.publicUrl, + siteName: siteConfig.name, +}; diff --git a/starterkits/saas/src/app/sitemap.ts b/starterkits/saas/src/app/sitemap.ts new file mode 100644 index 0000000..b0b09b9 --- /dev/null +++ b/starterkits/saas/src/app/sitemap.ts @@ -0,0 +1,29 @@ +import { publicRoutes, siteUrls } from "@/config/urls"; +import { getBlogs } from "@/server/actions/blog"; +import { getDocs } from "@/server/actions/docs"; +import type { MetadataRoute } from "next"; + +const addPathToBaseURL = (path: string) => `${siteUrls.publicUrl}${path}`; + +export default async function sitemap(): Promise { + const allBlogs = await getBlogs(); + + const blogs = allBlogs.map((blog) => ({ + url: addPathToBaseURL(`/${blog.metaData.slug}`), + lastModified: new Date(blog.metaData.updatedAt), + })); + + const allDocs = await getDocs(); + + const docs = allDocs.map((doc) => ({ + url: addPathToBaseURL(`/${doc.metaData.slug}`), + lastModified: new Date(doc.metaData.publishedAt), + })); + + const routes = publicRoutes.map((route) => ({ + url: addPathToBaseURL(route), + lastModified: new Date(), + })); + + return [...routes, ...blogs, ...docs]; +} diff --git a/starterkits/saas/src/app/waitlist/_constants/page-config.ts b/starterkits/saas/src/app/waitlist/_constants/page-config.ts new file mode 100644 index 0000000..ecaeb6e --- /dev/null +++ b/starterkits/saas/src/app/waitlist/_constants/page-config.ts @@ -0,0 +1,5 @@ +export const waitlistPageConfig = { + title: "Join the waitlist", + description: + "Welcome to Rapidlaunch, a platform which provides resources for building applications faster. We're currently working on adding more features and improving the user experience. In the meantime, you can join our waitlist!", +} as const; diff --git a/starterkits/saas/src/app/waitlist/page.tsx b/starterkits/saas/src/app/waitlist/page.tsx index f322bc8..cfab349 100644 --- a/starterkits/saas/src/app/waitlist/page.tsx +++ b/starterkits/saas/src/app/waitlist/page.tsx @@ -1,6 +1,13 @@ import { WaitlistForm } from "@/app/waitlist/_components/waitlist-form"; +import { waitlistPageConfig } from "@/app/waitlist/_constants/page-config"; import { Icons } from "@/components/ui/icons"; import { siteConfig } from "@/config/site"; +import { type Metadata } from "next"; + +export const metadata: Metadata = { + title: waitlistPageConfig.title, + description: waitlistPageConfig.description, +}; export default function Waitlist() { return ( diff --git a/starterkits/saas/src/config/site.ts b/starterkits/saas/src/config/site.ts index 9574491..c467552 100644 --- a/starterkits/saas/src/config/site.ts +++ b/starterkits/saas/src/config/site.ts @@ -5,9 +5,11 @@ */ export const siteConfig = { - name: "RapidLaunch", + name: "Rapidlaunch", description: - "Build your SaaS with ease, using our beautiful starterkit. Rapidlaunch is a powerful and flexible SaaS platform that allows you to build and deploy your SaaS quickly and easily.", + "Get your startup off the ground quickly with RapidLaunch! This open source Next.js starter kit provides the foundation you need to build your MVP fast – pre-built components, optimized performance, and ready-to-go styling", + orgImage: + "https://utfs.io/f/4ae0ddb1-4260-46f5-aa7c-70408cc192b9-aadavt.png", contactEmail: "hello@support.rapidlaunch.xyz", noReplyEmail: "Rapidlaunch@support.rapidlaunch.xyz", } as const; diff --git a/starterkits/saas/src/config/urls.ts b/starterkits/saas/src/config/urls.ts index fb069b8..d9c25bd 100644 --- a/starterkits/saas/src/config/urls.ts +++ b/starterkits/saas/src/config/urls.ts @@ -65,3 +65,23 @@ export const publicRoutes: string[] = [ siteUrls.waitlist, siteUrls.rapidlaunch, ]; + +export const protectedRoutes: string[] = [ + siteUrls.dashboard.home, + siteUrls.feedback, + siteUrls.organization.members.home, + siteUrls.organization.members.invite, + siteUrls.organization.settings, + siteUrls.organization.plansAndBilling, + siteUrls.auth.login, + siteUrls.auth.signup, + siteUrls.admin.dashboard, + siteUrls.admin.users, + siteUrls.admin.organizations, + siteUrls.admin.settings, + siteUrls.admin.waitlist, + siteUrls.admin.feedbacks, + siteUrls.admin.analytics, + siteUrls.profile.settings, + siteUrls.profile.billing, +]; diff --git a/starterkits/saas/src/content/blog/create-saas-in-1-day.mdx b/starterkits/saas/src/content/blog/create-saas-in-1-day.mdx index ff37e25..1ece858 100644 --- a/starterkits/saas/src/content/blog/create-saas-in-1-day.mdx +++ b/starterkits/saas/src/content/blog/create-saas-in-1-day.mdx @@ -1,6 +1,6 @@ --- title: Create a SaaS in 1 day -slug: create-saas-in-1-day +slug: /create-saas-in-1-day publishedAt: 2022-01-01 updatedAt: 2024-05-01 readTime: 5 min diff --git a/starterkits/saas/src/content/blog/introduction.mdx b/starterkits/saas/src/content/blog/introduction.mdx index 1f99c33..7b3d59d 100644 --- a/starterkits/saas/src/content/blog/introduction.mdx +++ b/starterkits/saas/src/content/blog/introduction.mdx @@ -1,7 +1,8 @@ --- title: Introduction -slug: introduction +slug: /introduction publishedAt: 2022-01-01 +updatedAt: 2024-05-01 readTime: 5 min tags: ["introduction", "saas"] description: This is the introduction diff --git a/starterkits/saas/src/content/changelogs/version-0.0.0.mdx b/starterkits/saas/src/content/changelogs/version-0.0.0.mdx index c2de931..9c5774a 100644 --- a/starterkits/saas/src/content/changelogs/version-0.0.0.mdx +++ b/starterkits/saas/src/content/changelogs/version-0.0.0.mdx @@ -1,6 +1,5 @@ --- title: "Launch of RapidLaunch" -slug: "launch-of-rapidlaunch" description: "RapidLaunch is a platform to create and launch SaaS products quickly." version: "0.0.0" publishedAt: 2024-04-01 diff --git a/starterkits/saas/src/content/changelogs/version-0.1.0.mdx b/starterkits/saas/src/content/changelogs/version-0.1.0.mdx index 3e3f2de..7ccbe3a 100644 --- a/starterkits/saas/src/content/changelogs/version-0.1.0.mdx +++ b/starterkits/saas/src/content/changelogs/version-0.1.0.mdx @@ -1,6 +1,5 @@ --- title: "Launch of RapidLaunch 2" -slug: "launch-of-rapidlaunch-2" description: "RapidLaunch is a platform to create and launch SaaS products quickly." version: "0.1.0" publishedAt: 2024-08-01 diff --git a/starterkits/saas/src/content/changelogs/version-0.2.4.mdx b/starterkits/saas/src/content/changelogs/version-0.2.4.mdx index 0a31bde..11018c6 100644 --- a/starterkits/saas/src/content/changelogs/version-0.2.4.mdx +++ b/starterkits/saas/src/content/changelogs/version-0.2.4.mdx @@ -1,6 +1,5 @@ --- title: "Launch of RapidLaunch 2" -slug: "launch-of-rapidlaunch-2" description: "RapidLaunch is a platform to create and launch SaaS products quickly." version: "0.4.0" publishedAt: 2024-10-06 diff --git a/starterkits/saas/src/content/docs/installation.mdx b/starterkits/saas/src/content/docs/installation.mdx index 81195b1..5d90ffe 100644 --- a/starterkits/saas/src/content/docs/installation.mdx +++ b/starterkits/saas/src/content/docs/installation.mdx @@ -1,7 +1,8 @@ --- title: "Installation" -slug: "getting-started/installation" +slug: "/getting-started/installation" description: "Learn how to install rapidlaunch" +publishedAt: 2024-01-01 --- Added your your own doumentation by create a .mdx file in "*src/content/docs*" folder. diff --git a/starterkits/saas/src/content/docs/introduction.mdx b/starterkits/saas/src/content/docs/introduction.mdx index 0de1cc8..2225ede 100644 --- a/starterkits/saas/src/content/docs/introduction.mdx +++ b/starterkits/saas/src/content/docs/introduction.mdx @@ -2,6 +2,7 @@ title: "Introduction" slug: "/" description: "Rapidlaunch is an open-source Next.js SaaS Starterkit/Boilerplate designed to expedite the development process of Software as a Service (SaaS) applications. Launch your MVP in days." +publishedAt: 2024-01-01 --- ## Creator diff --git a/starterkits/saas/src/middleware.ts b/starterkits/saas/src/middleware.ts index 6fdb8eb..64139de 100644 --- a/starterkits/saas/src/middleware.ts +++ b/starterkits/saas/src/middleware.ts @@ -1,7 +1,7 @@ import { getToken } from "next-auth/jwt"; import { NextResponse } from "next/server"; import type { NextRequest } from "next/server"; -import { publicRoutes, siteUrls } from "@/config/urls"; +import { protectedRoutes, siteUrls } from "@/config/urls"; import { getAbsoluteUrl } from "@/lib/utils"; import { env } from "@/env"; @@ -31,36 +31,42 @@ export async function middleware(request: NextRequest) { } /** if path is public route than do nothing */ - if ( - publicRoutes - .map((route) => route.startsWith(request.nextUrl.pathname)) - .includes(true) - ) { - return NextResponse.next(); - } + if (protectedRoutes.includes(request.nextUrl.pathname)) { + const session = await getToken({ req: request }); - const session = await getToken({ req: request }); + /** if path name starts from /auth, and session is there redirect to dashboard */ + if (session && request.nextUrl.pathname.startsWith("/auth")) { + return NextResponse.redirect( + getAbsoluteUrl(siteUrls.dashboard.home), + ); + } - /** if path name starts from /auth, and session is there redirect to dashboard */ - if (session && request.nextUrl.pathname.startsWith("/auth")) { - return NextResponse.redirect(getAbsoluteUrl(siteUrls.dashboard.home)); - } - - /** if path name does not start from /auth, and session is not there redirect to login */ - if (!session && !request.nextUrl.pathname.startsWith("/auth")) { - return NextResponse.redirect(getAbsoluteUrl(siteUrls.auth.login)); - } + /** if path name does not start from /auth, and session is not there redirect to login */ + if (!session && !request.nextUrl.pathname.startsWith("/auth")) { + return NextResponse.redirect(getAbsoluteUrl(siteUrls.auth.login)); + } - /** if path name start from admin, and session role is not admin or super admin redirect to dashboard */ - const isAdmin = - session?.role === "Admin" || session?.role === "Super Admin"; + /** if path name start from admin, and session role is not admin or super admin redirect to dashboard */ + const isAdmin = + session?.role === "Admin" || session?.role === "Super Admin"; - if (session && request.nextUrl.pathname.startsWith("/admin") && !isAdmin) { - return NextResponse.redirect(getAbsoluteUrl(siteUrls.dashboard.home)); + if ( + session && + request.nextUrl.pathname.startsWith("/admin") && + !isAdmin + ) { + return NextResponse.redirect( + getAbsoluteUrl(siteUrls.dashboard.home), + ); + } + } else { + return NextResponse.next(); } } // See "Matching Paths" below to learn more export const config = { - matcher: ["/((?!api|_next/static|_next/image|favicon.ico).*)"], + matcher: [ + "/((?!api|assets|_next/static|_next/image|favicon.ico|sitemap.xml|robots.txt).*)", + ], }; diff --git a/starterkits/saas/src/validations/mdx-content.ts b/starterkits/saas/src/validations/mdx-content.ts index 28da78f..c51ccde 100644 --- a/starterkits/saas/src/validations/mdx-content.ts +++ b/starterkits/saas/src/validations/mdx-content.ts @@ -3,6 +3,7 @@ import { z } from "zod"; export const docsMetaSchema = z.object({ title: z.string(), slug: z.string(), + publishedAt: z.date(), tags: z.array(z.string()).optional(), description: z.string().optional(), isDraft: z.boolean().optional(), @@ -14,7 +15,7 @@ export const blogMetaSchema = z.object({ title: z.string(), slug: z.string(), publishedAt: z.date(), - updatedAt: z.date().optional(), + updatedAt: z.date(), readTime: z.string(), tags: z.array(z.string()).optional(), description: z.string(), @@ -27,7 +28,6 @@ export type BlogMetaData = z.infer; export const changelogMetaSchema = z.object({ title: z.string(), - slug: z.string(), publishedAt: z.date(), thumbnail: z.string().url(), description: z.string(),