diff --git a/app/(auth)/layout.tsx b/app/(auth)/layout.tsx
deleted file mode 100644
index 2172f70..0000000
--- a/app/(auth)/layout.tsx
+++ /dev/null
@@ -1,7 +0,0 @@
-interface AuthLayoutProps {
- children: React.ReactNode
-}
-
-export default function AuthLayout({ children }: AuthLayoutProps) {
- return
{children}
-}
diff --git a/app/(auth)/login/page.tsx b/app/(auth)/login/page.tsx
deleted file mode 100644
index 501384c..0000000
--- a/app/(auth)/login/page.tsx
+++ /dev/null
@@ -1,51 +0,0 @@
-import { Metadata } from "next"
-import Link from "next/link"
-
-import { cn } from "@/lib/utils"
-import { buttonVariants } from "@/components/ui/button"
-import { Icons } from "@/components/icons"
-import { UserAuthForm } from "@/components/user-auth-form"
-
-export const metadata: Metadata = {
- title: "Login",
- description: "Login to your account",
-}
-
-export default function LoginPage() {
- return (
-
-
- <>
-
- Back
- >
-
-
-
-
-
- Welcome back
-
-
- Enter your email to sign in to your account
-
-
-
-
-
- Don't have an account? Sign Up
-
-
-
-
- )
-}
diff --git a/app/(auth)/register/page.tsx b/app/(auth)/register/page.tsx
deleted file mode 100644
index 1b61ff0..0000000
--- a/app/(auth)/register/page.tsx
+++ /dev/null
@@ -1,59 +0,0 @@
-import Link from "next/link"
-
-import { cn } from "@/lib/utils"
-import { buttonVariants } from "@/components/ui/button"
-import { Icons } from "@/components/icons"
-import { UserAuthForm } from "@/components/user-auth-form"
-
-export const metadata = {
- title: "Create an account",
- description: "Create an account to get started.",
-}
-
-export default function RegisterPage() {
- return (
-
-
- Login
-
-
-
-
-
-
-
- Create an account
-
-
- Enter your email below to create your account
-
-
-
-
- By clicking continue, you agree to our{" "}
-
- Terms of Service
- {" "}
- and{" "}
-
- Privacy Policy
-
- .
-
-
-
-
- )
-}
diff --git a/app/(dashboard)/dashboard/billing/loading.tsx b/app/(dashboard)/dashboard/billing/loading.tsx
deleted file mode 100644
index 5593522..0000000
--- a/app/(dashboard)/dashboard/billing/loading.tsx
+++ /dev/null
@@ -1,17 +0,0 @@
-import { CardSkeleton } from "@/components/card-skeleton"
-import { DashboardHeader } from "@/components/header"
-import { DashboardShell } from "@/components/shell"
-
-export default function DashboardBillingLoading() {
- return (
-
-
-
-
-
-
- )
-}
diff --git a/app/(dashboard)/dashboard/billing/page.tsx b/app/(dashboard)/dashboard/billing/page.tsx
deleted file mode 100644
index 0a601e5..0000000
--- a/app/(dashboard)/dashboard/billing/page.tsx
+++ /dev/null
@@ -1,76 +0,0 @@
-import { redirect } from "next/navigation"
-
-import { authOptions } from "@/lib/auth"
-import { getCurrentUser } from "@/lib/session"
-import { stripe } from "@/lib/stripe"
-import { getUserSubscriptionPlan } from "@/lib/subscription"
-import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"
-import {
- Card,
- CardContent,
- CardDescription,
- CardHeader,
- CardTitle,
-} from "@/components/ui/card"
-import { BillingForm } from "@/components/billing-form"
-import { DashboardHeader } from "@/components/header"
-import { Icons } from "@/components/icons"
-import { DashboardShell } from "@/components/shell"
-
-export const metadata = {
- title: "Billing",
- description: "Manage billing and your subscription plan.",
-}
-
-export default async function BillingPage() {
- const user = await getCurrentUser()
-
- if (!user) {
- redirect(authOptions?.pages?.signIn || "/login")
- }
-
- const subscriptionPlan = await getUserSubscriptionPlan(user.id)
-
- // If user has a pro plan, check cancel status on Stripe.
- let isCanceled = false
- if (subscriptionPlan.isPro && subscriptionPlan.stripeSubscriptionId) {
- const stripePlan = await stripe.subscriptions.retrieve(
- subscriptionPlan.stripeSubscriptionId
- )
- isCanceled = stripePlan.cancel_at_period_end
- }
-
- return (
-
-
-
-
-
- This is a demo app.
-
- Taxonomy app is a demo app using a Stripe test environment. You can
- find a list of test card numbers on the{" "}
-
- Stripe docs
-
- .
-
-
-
-
-
- )
-}
diff --git a/app/(dashboard)/dashboard/layout.tsx b/app/(dashboard)/dashboard/layout.tsx
deleted file mode 100644
index 71a90d6..0000000
--- a/app/(dashboard)/dashboard/layout.tsx
+++ /dev/null
@@ -1,48 +0,0 @@
-import { notFound } from "next/navigation"
-
-import { dashboardConfig } from "@/config/dashboard"
-import { getCurrentUser } from "@/lib/session"
-import { MainNav } from "@/components/main-nav"
-import { DashboardNav } from "@/components/nav"
-import { SiteFooter } from "@/components/site-footer"
-import { UserAccountNav } from "@/components/user-account-nav"
-
-interface DashboardLayoutProps {
- children?: React.ReactNode
-}
-
-export default async function DashboardLayout({
- children,
-}: DashboardLayoutProps) {
- const user = await getCurrentUser()
-
- if (!user) {
- return notFound()
- }
-
- return (
-
- )
-}
diff --git a/app/(dashboard)/dashboard/loading.tsx b/app/(dashboard)/dashboard/loading.tsx
deleted file mode 100644
index 72bd402..0000000
--- a/app/(dashboard)/dashboard/loading.tsx
+++ /dev/null
@@ -1,21 +0,0 @@
-import { DashboardHeader } from "@/components/header"
-import { PostCreateButton } from "@/components/post-create-button"
-import { PostItem } from "@/components/post-item"
-import { DashboardShell } from "@/components/shell"
-
-export default function DashboardLoading() {
- return (
-
-
-
-
-
-
- )
-}
diff --git a/app/(dashboard)/dashboard/page.tsx b/app/(dashboard)/dashboard/page.tsx
deleted file mode 100644
index 5f4fc6e..0000000
--- a/app/(dashboard)/dashboard/page.tsx
+++ /dev/null
@@ -1,63 +0,0 @@
-import { redirect } from "next/navigation"
-
-import { authOptions } from "@/lib/auth"
-import { db } from "@/lib/db"
-import { getCurrentUser } from "@/lib/session"
-import { EmptyPlaceholder } from "@/components/empty-placeholder"
-import { DashboardHeader } from "@/components/header"
-import { PostCreateButton } from "@/components/post-create-button"
-import { PostItem } from "@/components/post-item"
-import { DashboardShell } from "@/components/shell"
-
-export const metadata = {
- title: "Dashboard",
-}
-
-export default async function DashboardPage() {
- const user = await getCurrentUser()
-
- if (!user) {
- redirect(authOptions?.pages?.signIn || "/login")
- }
-
- const posts = await db.post.findMany({
- where: {
- authorId: user.id,
- },
- select: {
- id: true,
- title: true,
- published: true,
- createdAt: true,
- },
- orderBy: {
- updatedAt: "desc",
- },
- })
-
- return (
-
-
-
-
-
- {posts?.length ? (
-
- {posts.map((post) => (
-
- ))}
-
- ) : (
-
-
- No posts created
-
- You don't have any posts yet. Start creating content.
-
-
-
- )}
-
-
- )
-}
diff --git a/app/(dashboard)/dashboard/settings/loading.tsx b/app/(dashboard)/dashboard/settings/loading.tsx
deleted file mode 100644
index 8f9eacb..0000000
--- a/app/(dashboard)/dashboard/settings/loading.tsx
+++ /dev/null
@@ -1,18 +0,0 @@
-import { Card } from "@/components/ui/card"
-import { CardSkeleton } from "@/components/card-skeleton"
-import { DashboardHeader } from "@/components/header"
-import { DashboardShell } from "@/components/shell"
-
-export default function DashboardSettingsLoading() {
- return (
-
-
-
-
-
-
- )
-}
diff --git a/app/(dashboard)/dashboard/settings/page.tsx b/app/(dashboard)/dashboard/settings/page.tsx
deleted file mode 100644
index 6e8545d..0000000
--- a/app/(dashboard)/dashboard/settings/page.tsx
+++ /dev/null
@@ -1,34 +0,0 @@
-import { redirect } from "next/navigation"
-
-import { authOptions } from "@/lib/auth"
-import { getCurrentUser } from "@/lib/session"
-import { DashboardHeader } from "@/components/header"
-import { DashboardShell } from "@/components/shell"
-import { UserNameForm } from "@/components/user-name-form"
-
-export const metadata = {
- title: "Settings",
- description: "Manage account and website settings.",
-}
-
-export default async function SettingsPage() {
- const user = await getCurrentUser()
-
- if (!user) {
- redirect(authOptions?.pages?.signIn || "/login")
- }
-
- return (
-
-
-
- {user?.name ? (
-
- ) : null}
-
-
- )
-}
diff --git a/app/(docs)/docs/[[...slug]]/page.tsx b/app/(docs)/docs/[[...slug]]/page.tsx
deleted file mode 100644
index 051d113..0000000
--- a/app/(docs)/docs/[[...slug]]/page.tsx
+++ /dev/null
@@ -1,106 +0,0 @@
-import { notFound } from "next/navigation"
-import { allDocs } from "contentlayer/generated"
-
-import { getTableOfContents } from "@/lib/toc"
-import { Mdx } from "@/components/mdx-components"
-import { DocsPageHeader } from "@/components/page-header"
-import { DocsPager } from "@/components/pager"
-import { DashboardTableOfContents } from "@/components/toc"
-
-import "@/styles/mdx.css"
-import { Metadata } from "next"
-
-import { absoluteUrl } from "@/lib/utils"
-
-interface DocPageProps {
- params: {
- slug: string[]
- }
-}
-
-async function getDocFromParams(params) {
- const slug = params.slug?.join("/") || ""
- const doc = allDocs.find((doc) => doc.slugAsParams === slug)
-
- if (!doc) {
- null
- }
-
- return doc
-}
-
-export async function generateMetadata({
- params,
-}: DocPageProps): Promise {
- const doc = await getDocFromParams(params)
-
- if (!doc) {
- return {}
- }
-
- const url = process.env.NEXT_PUBLIC_APP_URL
-
- const ogUrl = new URL(`${url}/api/og`)
- ogUrl.searchParams.set("heading", doc.description ?? doc.title)
- ogUrl.searchParams.set("type", "Documentation")
- ogUrl.searchParams.set("mode", "dark")
-
- return {
- title: doc.title,
- description: doc.description,
- openGraph: {
- title: doc.title,
- description: doc.description,
- type: "article",
- url: absoluteUrl(doc.slug),
- images: [
- {
- url: ogUrl.toString(),
- width: 1200,
- height: 630,
- alt: doc.title,
- },
- ],
- },
- twitter: {
- card: "summary_large_image",
- title: doc.title,
- description: doc.description,
- images: [ogUrl.toString()],
- },
- }
-}
-
-export async function generateStaticParams(): Promise<
- DocPageProps["params"][]
-> {
- return allDocs.map((doc) => ({
- slug: doc.slugAsParams.split("/"),
- }))
-}
-
-export default async function DocPage({ params }: DocPageProps) {
- const doc = await getDocFromParams(params)
-
- if (!doc) {
- notFound()
- }
-
- const toc = await getTableOfContents(doc.body.raw)
-
- return (
-
-
-
-
-
-
-
-
-
- )
-}
diff --git a/app/(docs)/docs/layout.tsx b/app/(docs)/docs/layout.tsx
deleted file mode 100644
index 38d92a5..0000000
--- a/app/(docs)/docs/layout.tsx
+++ /dev/null
@@ -1,17 +0,0 @@
-import { docsConfig } from "@/config/docs"
-import { DocsSidebarNav } from "@/components/sidebar-nav"
-
-interface DocsLayoutProps {
- children: React.ReactNode
-}
-
-export default function DocsLayout({ children }: DocsLayoutProps) {
- return (
-
- )
-}
diff --git a/app/(docs)/guides/[...slug]/page.tsx b/app/(docs)/guides/[...slug]/page.tsx
deleted file mode 100644
index 11c49e1..0000000
--- a/app/(docs)/guides/[...slug]/page.tsx
+++ /dev/null
@@ -1,116 +0,0 @@
-import Link from "next/link"
-import { notFound } from "next/navigation"
-import { allGuides } from "contentlayer/generated"
-
-import { getTableOfContents } from "@/lib/toc"
-import { Icons } from "@/components/icons"
-import { Mdx } from "@/components/mdx-components"
-import { DocsPageHeader } from "@/components/page-header"
-import { DashboardTableOfContents } from "@/components/toc"
-
-import "@/styles/mdx.css"
-import { Metadata } from "next"
-
-import { absoluteUrl, cn } from "@/lib/utils"
-import { buttonVariants } from "@/components/ui/button"
-
-interface GuidePageProps {
- params: {
- slug: string[]
- }
-}
-
-async function getGuideFromParams(params) {
- const slug = params?.slug?.join("/")
- const guide = allGuides.find((guide) => guide.slugAsParams === slug)
-
- if (!guide) {
- null
- }
-
- return guide
-}
-
-export async function generateMetadata({
- params,
-}: GuidePageProps): Promise {
- const guide = await getGuideFromParams(params)
-
- if (!guide) {
- return {}
- }
-
- const url = process.env.NEXT_PUBLIC_APP_URL
-
- const ogUrl = new URL(`${url}/api/og`)
- ogUrl.searchParams.set("heading", guide.title)
- ogUrl.searchParams.set("type", "Guide")
- ogUrl.searchParams.set("mode", "dark")
-
- return {
- title: guide.title,
- description: guide.description,
- openGraph: {
- title: guide.title,
- description: guide.description,
- type: "article",
- url: absoluteUrl(guide.slug),
- images: [
- {
- url: ogUrl.toString(),
- width: 1200,
- height: 630,
- alt: guide.title,
- },
- ],
- },
- twitter: {
- card: "summary_large_image",
- title: guide.title,
- description: guide.description,
- images: [ogUrl.toString()],
- },
- }
-}
-
-export async function generateStaticParams(): Promise<
- GuidePageProps["params"][]
-> {
- return allGuides.map((guide) => ({
- slug: guide.slugAsParams.split("/"),
- }))
-}
-
-export default async function GuidePage({ params }: GuidePageProps) {
- const guide = await getGuideFromParams(params)
-
- if (!guide) {
- notFound()
- }
-
- const toc = await getTableOfContents(guide.body.raw)
-
- return (
-
-
-
-
-
-
-
-
- See all guides
-
-
-
-
-
- )
-}
diff --git a/app/(docs)/guides/layout.tsx b/app/(docs)/guides/layout.tsx
deleted file mode 100644
index d708c74..0000000
--- a/app/(docs)/guides/layout.tsx
+++ /dev/null
@@ -1,7 +0,0 @@
-interface GuidesLayoutProps {
- children: React.ReactNode
-}
-
-export default function GuidesLayout({ children }: GuidesLayoutProps) {
- return {children}
-}
diff --git a/app/(docs)/guides/page.tsx b/app/(docs)/guides/page.tsx
deleted file mode 100644
index 9d0ebd0..0000000
--- a/app/(docs)/guides/page.tsx
+++ /dev/null
@@ -1,65 +0,0 @@
-import Link from "next/link"
-import { allGuides } from "contentlayer/generated"
-import { compareDesc } from "date-fns"
-
-import { formatDate } from "@/lib/utils"
-import { DocsPageHeader } from "@/components/page-header"
-
-export const metadata = {
- title: "Guides",
- description:
- "This section includes end-to-end guides for developing Next.js 13 apps.",
-}
-
-export default function GuidesPage() {
- const guides = allGuides
- .filter((guide) => guide.published)
- .sort((a, b) => {
- return compareDesc(new Date(a.date), new Date(b.date))
- })
-
- return (
-
-
- {guides?.length ? (
-
- {guides.map((guide) => (
-
- {guide.featured && (
-
- Featured
-
- )}
-
-
-
- {guide.title}
-
- {guide.description && (
-
{guide.description}
- )}
-
- {guide.date && (
-
- {formatDate(guide.date)}
-
- )}
-
-
- View
-
-
- ))}
-
- ) : (
-
No guides published.
- )}
-
- )
-}
diff --git a/app/(docs)/layout.tsx b/app/(docs)/layout.tsx
deleted file mode 100644
index 8c76140..0000000
--- a/app/(docs)/layout.tsx
+++ /dev/null
@@ -1,44 +0,0 @@
-import Link from "next/link"
-
-import { docsConfig } from "@/config/docs"
-import { siteConfig } from "@/config/site"
-import { Icons } from "@/components/icons"
-import { MainNav } from "@/components/main-nav"
-import { DocsSearch } from "@/components/search"
-import { DocsSidebarNav } from "@/components/sidebar-nav"
-import { SiteFooter } from "@/components/site-footer"
-
-interface DocsLayoutProps {
- children: React.ReactNode
-}
-
-export default function DocsLayout({ children }: DocsLayoutProps) {
- return (
-
-
-
-
-
-
-
-
-
-
-
-
-
- GitHub
-
-
-
-
-
-
{children}
-
-
- )
-}
diff --git a/app/(editor)/editor/[postId]/loading.tsx b/app/(editor)/editor/[postId]/loading.tsx
deleted file mode 100644
index e4f6e95..0000000
--- a/app/(editor)/editor/[postId]/loading.tsx
+++ /dev/null
@@ -1,18 +0,0 @@
-import { Skeleton } from "@/components/ui/skeleton"
-
-export default function Loading() {
- return (
-
- )
-}
diff --git a/app/(editor)/editor/[postId]/not-found.tsx b/app/(editor)/editor/[postId]/not-found.tsx
deleted file mode 100644
index 37d479e..0000000
--- a/app/(editor)/editor/[postId]/not-found.tsx
+++ /dev/null
@@ -1,19 +0,0 @@
-import Link from "next/link"
-
-import { buttonVariants } from "@/components/ui/button"
-import { EmptyPlaceholder } from "@/components/empty-placeholder"
-
-export default function NotFound() {
- return (
-
-
- Uh oh! Not Found
-
- This post cound not be found. Please try again.
-
-
- Go to Dashboard
-
-
- )
-}
diff --git a/app/(editor)/editor/[postId]/page.tsx b/app/(editor)/editor/[postId]/page.tsx
deleted file mode 100644
index 784adda..0000000
--- a/app/(editor)/editor/[postId]/page.tsx
+++ /dev/null
@@ -1,45 +0,0 @@
-import { notFound, redirect } from "next/navigation"
-import { Post, User } from "@prisma/client"
-
-import { authOptions } from "@/lib/auth"
-import { db } from "@/lib/db"
-import { getCurrentUser } from "@/lib/session"
-import { Editor } from "@/components/editor"
-
-async function getPostForUser(postId: Post["id"], userId: User["id"]) {
- return await db.post.findFirst({
- where: {
- id: postId,
- authorId: userId,
- },
- })
-}
-
-interface EditorPageProps {
- params: { postId: string }
-}
-
-export default async function EditorPage({ params }: EditorPageProps) {
- const user = await getCurrentUser()
-
- if (!user) {
- redirect(authOptions?.pages?.signIn || "/login")
- }
-
- const post = await getPostForUser(params.postId, user.id)
-
- if (!post) {
- notFound()
- }
-
- return (
-
- )
-}
diff --git a/app/(editor)/editor/layout.tsx b/app/(editor)/editor/layout.tsx
deleted file mode 100644
index a9a1ce9..0000000
--- a/app/(editor)/editor/layout.tsx
+++ /dev/null
@@ -1,11 +0,0 @@
-interface EditorProps {
- children?: React.ReactNode
-}
-
-export default function EditorLayout({ children }: EditorProps) {
- return (
-
- {children}
-
- )
-}
diff --git a/app/(marketing)/[...slug]/page.tsx b/app/(marketing)/[...slug]/page.tsx
deleted file mode 100644
index 6219cc1..0000000
--- a/app/(marketing)/[...slug]/page.tsx
+++ /dev/null
@@ -1,98 +0,0 @@
-import { notFound } from "next/navigation"
-import { allPages } from "contentlayer/generated"
-
-import { Mdx } from "@/components/mdx-components"
-
-import "@/styles/mdx.css"
-import { Metadata } from "next"
-
-import { siteConfig } from "@/config/site"
-import { absoluteUrl } from "@/lib/utils"
-
-interface PageProps {
- params: {
- slug: string[]
- }
-}
-
-async function getPageFromParams(params) {
- const slug = params?.slug?.join("/")
- const page = allPages.find((page) => page.slugAsParams === slug)
-
- if (!page) {
- null
- }
-
- return page
-}
-
-export async function generateMetadata({
- params,
-}: PageProps): Promise {
- const page = await getPageFromParams(params)
-
- if (!page) {
- return {}
- }
-
- const url = process.env.NEXT_PUBLIC_APP_URL
-
- const ogUrl = new URL(`${url}/api/og`)
- ogUrl.searchParams.set("heading", page.title)
- ogUrl.searchParams.set("type", siteConfig.name)
- ogUrl.searchParams.set("mode", "light")
-
- return {
- title: page.title,
- description: page.description,
- openGraph: {
- title: page.title,
- description: page.description,
- type: "article",
- url: absoluteUrl(page.slug),
- images: [
- {
- url: ogUrl.toString(),
- width: 1200,
- height: 630,
- alt: page.title,
- },
- ],
- },
- twitter: {
- card: "summary_large_image",
- title: page.title,
- description: page.description,
- images: [ogUrl.toString()],
- },
- }
-}
-
-export async function generateStaticParams(): Promise {
- return allPages.map((page) => ({
- slug: page.slugAsParams.split("/"),
- }))
-}
-
-export default async function PagePage({ params }: PageProps) {
- const page = await getPageFromParams(params)
-
- if (!page) {
- notFound()
- }
-
- return (
-
-
-
- {page.title}
-
- {page.description && (
-
{page.description}
- )}
-
-
-
-
- )
-}
diff --git a/app/(marketing)/blog/[...slug]/page.tsx b/app/(marketing)/blog/[...slug]/page.tsx
deleted file mode 100644
index 03fa2ad..0000000
--- a/app/(marketing)/blog/[...slug]/page.tsx
+++ /dev/null
@@ -1,168 +0,0 @@
-import { notFound } from "next/navigation"
-import { allAuthors, allPosts } from "contentlayer/generated"
-
-import { Mdx } from "@/components/mdx-components"
-
-import "@/styles/mdx.css"
-import { Metadata } from "next"
-import Image from "next/image"
-import Link from "next/link"
-
-import { absoluteUrl, cn, formatDate } from "@/lib/utils"
-import { buttonVariants } from "@/components/ui/button"
-import { Icons } from "@/components/icons"
-
-interface PostPageProps {
- params: {
- slug: string[]
- }
-}
-
-async function getPostFromParams(params) {
- const slug = params?.slug?.join("/")
- const post = allPosts.find((post) => post.slugAsParams === slug)
-
- if (!post) {
- null
- }
-
- return post
-}
-
-export async function generateMetadata({
- params,
-}: PostPageProps): Promise {
- const post = await getPostFromParams(params)
-
- if (!post) {
- return {}
- }
-
- const url = process.env.NEXT_PUBLIC_APP_URL
-
- const ogUrl = new URL(`${url}/api/og`)
- ogUrl.searchParams.set("heading", post.title)
- ogUrl.searchParams.set("type", "Blog Post")
- ogUrl.searchParams.set("mode", "dark")
-
- return {
- title: post.title,
- description: post.description,
- authors: post.authors.map((author) => ({
- name: author,
- })),
- openGraph: {
- title: post.title,
- description: post.description,
- type: "article",
- url: absoluteUrl(post.slug),
- images: [
- {
- url: ogUrl.toString(),
- width: 1200,
- height: 630,
- alt: post.title,
- },
- ],
- },
- twitter: {
- card: "summary_large_image",
- title: post.title,
- description: post.description,
- images: [ogUrl.toString()],
- },
- }
-}
-
-export async function generateStaticParams(): Promise<
- PostPageProps["params"][]
-> {
- return allPosts.map((post) => ({
- slug: post.slugAsParams.split("/"),
- }))
-}
-
-export default async function PostPage({ params }: PostPageProps) {
- const post = await getPostFromParams(params)
-
- if (!post) {
- notFound()
- }
-
- const authors = post.authors.map((author) =>
- allAuthors.find(({ slug }) => slug === `/authors/${author}`)
- )
-
- return (
-
-
-
- See all posts
-
-
- {post.date && (
-
- Published on {formatDate(post.date)}
-
- )}
-
- {post.title}
-
- {authors?.length ? (
-
- {authors.map((author) =>
- author ? (
-
-
-
-
{author.title}
-
- @{author.twitter}
-
-
-
- ) : null
- )}
-
- ) : null}
-
- {post.image && (
-
- )}
-
-
-
-
-
- See all posts
-
-
-
- )
-}
diff --git a/app/(marketing)/blog/page.tsx b/app/(marketing)/blog/page.tsx
deleted file mode 100644
index 8dc2df1..0000000
--- a/app/(marketing)/blog/page.tsx
+++ /dev/null
@@ -1,69 +0,0 @@
-import Image from "next/image"
-import Link from "next/link"
-import { allPosts } from "contentlayer/generated"
-import { compareDesc } from "date-fns"
-
-import { formatDate } from "@/lib/utils"
-
-export const metadata = {
- title: "Blog",
-}
-
-export default async function BlogPage() {
- const posts = allPosts
- .filter((post) => post.published)
- .sort((a, b) => {
- return compareDesc(new Date(a.date), new Date(b.date))
- })
-
- return (
-
-
-
-
- Blog
-
-
- A blog built using Contentlayer. Posts are written in MDX.
-
-
-
-
- {posts?.length ? (
-
- {posts.map((post, index) => (
-
- {post.image && (
-
- )}
- {post.title}
- {post.description && (
- {post.description}
- )}
- {post.date && (
-
- {formatDate(post.date)}
-
- )}
-
- View Article
-
-
- ))}
-
- ) : (
-
No posts published.
- )}
-
- )
-}
diff --git a/app/(marketing)/layout.tsx b/app/(marketing)/layout.tsx
deleted file mode 100644
index 902099c..0000000
--- a/app/(marketing)/layout.tsx
+++ /dev/null
@@ -1,38 +0,0 @@
-import Link from "next/link"
-
-import { marketingConfig } from "@/config/marketing"
-import { cn } from "@/lib/utils"
-import { buttonVariants } from "@/components/ui/button"
-import { MainNav } from "@/components/main-nav"
-import { SiteFooter } from "@/components/site-footer"
-
-interface MarketingLayoutProps {
- children: React.ReactNode
-}
-
-export default async function MarketingLayout({
- children,
-}: MarketingLayoutProps) {
- return (
-
- )
-}
diff --git a/app/(marketing)/page.tsx b/app/(marketing)/page.tsx
deleted file mode 100644
index 3467b03..0000000
--- a/app/(marketing)/page.tsx
+++ /dev/null
@@ -1,225 +0,0 @@
-import Link from "next/link"
-
-import { siteConfig } from "@/config/site"
-import { cn } from "@/lib/utils"
-import { buttonVariants } from "@/components/ui/button"
-
-async function getGitHubStars(): Promise {
- try {
- const response = await fetch(
- "https://api.github.com/repos/shadcn/taxonomy",
- {
- headers: {
- Accept: "application/vnd.github+json",
- Authorization: `Bearer ${process.env.GITHUB_ACCESS_TOKEN}`,
- },
- next: {
- revalidate: 60,
- },
- }
- )
-
- if (!response?.ok) {
- return null
- }
-
- const json = await response.json()
-
- return parseInt(json["stargazers_count"]).toLocaleString()
- } catch (error) {
- return null
- }
-}
-
-export default async function IndexPage() {
- const stars = await getGitHubStars()
-
- return (
- <>
-
-
-
- Follow along on Twitter
-
-
- An example app built using Next.js 13 server components.
-
-
- I'm building a web app with Next.js 13 and open sourcing
- everything. Follow along as we figure this out together.
-
-
-
- Get Started
-
-
- GitHub
-
-
-
-
-
-
-
- Features
-
-
- This project is an experiment to see how a modern app, with features
- like auth, subscriptions, API routes, and static pages would work in
- Next.js 13 app dir.
-
-
-
-
-
-
-
-
-
-
Next.js 13
-
- App dir, Routing, Layouts, Loading UI and API routes.
-
-
-
-
-
-
-
-
-
-
-
React 18
-
- Server and Client Components. Use hook.
-
-
-
-
-
-
-
-
-
-
-
Database
-
- ORM using Prisma and deployed on PlanetScale.
-
-
-
-
-
-
-
-
-
-
-
Components
-
- UI components built using Radix UI and styled with Tailwind
- CSS.
-
-
-
-
-
-
-
-
-
-
-
Authentication
-
- Authentication using NextAuth.js and middlewares.
-
-
-
-
-
-
-
-
-
-
-
Subscriptions
-
- Free and paid subscriptions using Stripe.
-
-
-
-
-
-
-
- Taxonomy also includes a blog and a full-featured documentation site
- built using Contentlayer and MDX.
-
-
-
-
-
-
- Proudly Open Source
-
-
- Taxonomy is open source and powered by open source software. {" "}
- The code is available on{" "}
-
- GitHub
-
- .{" "}
-
- {stars && (
-
-
-
-
-
- {stars} stars on GitHub
-
-
-
- )}
-
-
- >
- )
-}
diff --git a/app/(marketing)/pricing/page.tsx b/app/(marketing)/pricing/page.tsx
deleted file mode 100644
index 505f14c..0000000
--- a/app/(marketing)/pricing/page.tsx
+++ /dev/null
@@ -1,69 +0,0 @@
-import Link from "next/link"
-
-import { cn } from "@/lib/utils"
-import { buttonVariants } from "@/components/ui/button"
-import { Icons } from "@/components/icons"
-
-export const metadata = {
- title: "Pricing",
-}
-
-export default function PricingPage() {
- return (
-
-
-
- Simple, transparent pricing
-
-
- Unlock all features including unlimited posts for your blog.
-
-
-
-
-
- What's included in the PRO plan
-
-
-
- Unlimited Posts
-
-
- Unlimited Users
-
-
-
- Custom domain
-
-
- Dashboard Analytics
-
-
- Access to Discord
-
-
- Premium Support
-
-
-
-
-
-
$19
-
- Billed Monthly
-
-
-
- Get Started
-
-
-
-
-
- Taxonomy is a demo app.{" "}
- You can test the upgrade and won't be charged.
-
-
-
- )
-}
diff --git a/app/api/og/route.tsx b/app/api/og/route.tsx
deleted file mode 100644
index d674f3c..0000000
--- a/app/api/og/route.tsx
+++ /dev/null
@@ -1,148 +0,0 @@
-import { ImageResponse } from "@vercel/og"
-
-import { ogImageSchema } from "@/lib/validations/og"
-
-export const runtime = "edge"
-
-const interRegular = fetch(
- new URL("../../../assets/fonts/Inter-Regular.ttf", import.meta.url)
-).then((res) => res.arrayBuffer())
-
-const interBold = fetch(
- new URL("../../../assets/fonts/CalSans-SemiBold.ttf", import.meta.url)
-).then((res) => res.arrayBuffer())
-
-export async function GET(req: Request) {
- try {
- const fontRegular = await interRegular
- const fontBold = await interBold
-
- const url = new URL(req.url)
- const values = ogImageSchema.parse(Object.fromEntries(url.searchParams))
- const heading =
- values.heading.length > 140
- ? `${values.heading.substring(0, 140)}...`
- : values.heading
-
- const { mode } = values
- const paint = mode === "dark" ? "#fff" : "#000"
-
- const fontSize = heading.length > 100 ? "70px" : "100px"
-
- return new ImageResponse(
- (
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {values.type}
-
-
- {heading}
-
-
-
-
- tx.shadcn.com
-
-
-
-
-
-
-
github.com/shadcn/taxonomy
-
-
-
- ),
- {
- width: 1200,
- height: 630,
- fonts: [
- {
- name: "Inter",
- data: fontRegular,
- weight: 400,
- style: "normal",
- },
- {
- name: "Cal Sans",
- data: fontBold,
- weight: 700,
- style: "normal",
- },
- ],
- }
- )
- } catch (error) {
- return new Response(`Failed to generate image`, {
- status: 500,
- })
- }
-}
diff --git a/app/api/posts/[postId]/route.ts b/app/api/posts/[postId]/route.ts
deleted file mode 100644
index 0614640..0000000
--- a/app/api/posts/[postId]/route.ts
+++ /dev/null
@@ -1,93 +0,0 @@
-import { getServerSession } from "next-auth"
-import * as z from "zod"
-
-import { authOptions } from "@/lib/auth"
-import { db } from "@/lib/db"
-import { postPatchSchema } from "@/lib/validations/post"
-
-const routeContextSchema = z.object({
- params: z.object({
- postId: z.string(),
- }),
-})
-
-export async function DELETE(
- req: Request,
- context: z.infer
-) {
- try {
- // Validate the route params.
- const { params } = routeContextSchema.parse(context)
-
- // Check if the user has access to this post.
- if (!(await verifyCurrentUserHasAccessToPost(params.postId))) {
- return new Response(null, { status: 403 })
- }
-
- // Delete the post.
- await db.post.delete({
- where: {
- id: params.postId as string,
- },
- })
-
- return new Response(null, { status: 204 })
- } catch (error) {
- if (error instanceof z.ZodError) {
- return new Response(JSON.stringify(error.issues), { status: 422 })
- }
-
- return new Response(null, { status: 500 })
- }
-}
-
-export async function PATCH(
- req: Request,
- context: z.infer
-) {
- try {
- // Validate route params.
- const { params } = routeContextSchema.parse(context)
-
- // Check if the user has access to this post.
- if (!(await verifyCurrentUserHasAccessToPost(params.postId))) {
- return new Response(null, { status: 403 })
- }
-
- // Get the request body and validate it.
- const json = await req.json()
- const body = postPatchSchema.parse(json)
-
- // Update the post.
- // TODO: Implement sanitization for content.
- await db.post.update({
- where: {
- id: params.postId,
- },
- data: {
- title: body.title,
- content: body.content,
- },
- })
-
- return new Response(null, { status: 200 })
- } catch (error) {
- if (error instanceof z.ZodError) {
- return new Response(JSON.stringify(error.issues), { status: 422 })
- }
-
- return new Response(null, { status: 500 })
- }
-}
-
-async function verifyCurrentUserHasAccessToPost(postId: string) {
- const session = await getServerSession(authOptions)
- const count = await db.post.count({
- where: {
- id: postId,
- authorId: session?.user.id,
- },
- })
-
- return count > 0
-}
diff --git a/app/api/posts/route.ts b/app/api/posts/route.ts
deleted file mode 100644
index 7c4c8bb..0000000
--- a/app/api/posts/route.ts
+++ /dev/null
@@ -1,92 +0,0 @@
-import { getServerSession } from "next-auth/next"
-import * as z from "zod"
-
-import { authOptions } from "@/lib/auth"
-import { db } from "@/lib/db"
-import { RequiresProPlanError } from "@/lib/exceptions"
-import { getUserSubscriptionPlan } from "@/lib/subscription"
-
-const postCreateSchema = z.object({
- title: z.string(),
- content: z.string().optional(),
-})
-
-export async function GET() {
- try {
- const session = await getServerSession(authOptions)
-
- if (!session) {
- return new Response("Unauthorized", { status: 403 })
- }
-
- const { user } = session
- const posts = await db.post.findMany({
- select: {
- id: true,
- title: true,
- published: true,
- createdAt: true,
- },
- where: {
- authorId: user.id,
- },
- })
-
- return new Response(JSON.stringify(posts))
- } catch (error) {
- return new Response(null, { status: 500 })
- }
-}
-
-export async function POST(req: Request) {
- try {
- const session = await getServerSession(authOptions)
-
- if (!session) {
- return new Response("Unauthorized", { status: 403 })
- }
-
- const { user } = session
- const subscriptionPlan = await getUserSubscriptionPlan(user.id)
-
- // If user is on a free plan.
- // Check if user has reached limit of 3 posts.
- if (!subscriptionPlan?.isPro) {
- const count = await db.post.count({
- where: {
- authorId: user.id,
- },
- })
-
- if (count >= 3) {
- throw new RequiresProPlanError()
- }
- }
-
- const json = await req.json()
- const body = postCreateSchema.parse(json)
-
- const post = await db.post.create({
- data: {
- title: body.title,
- content: body.content,
- authorId: session.user.id,
- },
- select: {
- id: true,
- },
- })
-
- return new Response(JSON.stringify(post))
- } catch (error) {
- if (error instanceof z.ZodError) {
- return new Response(JSON.stringify(error.issues), { status: 422 })
- }
-
- if (error instanceof RequiresProPlanError) {
- return new Response("Requires Pro Plan", { status: 402 })
- }
-
- return new Response(null, { status: 500 })
- }
-}
diff --git a/app/api/users/[userId]/route.ts b/app/api/users/[userId]/route.ts
deleted file mode 100644
index 6b68bad..0000000
--- a/app/api/users/[userId]/route.ts
+++ /dev/null
@@ -1,50 +0,0 @@
-import { getServerSession } from "next-auth/next"
-import { z } from "zod"
-
-import { authOptions } from "@/lib/auth"
-import { db } from "@/lib/db"
-import { userNameSchema } from "@/lib/validations/user"
-
-const routeContextSchema = z.object({
- params: z.object({
- userId: z.string(),
- }),
-})
-
-export async function PATCH(
- req: Request,
- context: z.infer
-) {
- try {
- // Validate the route context.
- const { params } = routeContextSchema.parse(context)
-
- // Ensure user is authentication and has access to this user.
- const session = await getServerSession(authOptions)
- if (!session?.user || params.userId !== session?.user.id) {
- return new Response(null, { status: 403 })
- }
-
- // Get the request body and validate it.
- const body = await req.json()
- const payload = userNameSchema.parse(body)
-
- // Update the user.
- await db.user.update({
- where: {
- id: session.user.id,
- },
- data: {
- name: payload.name,
- },
- })
-
- return new Response(null, { status: 200 })
- } catch (error) {
- if (error instanceof z.ZodError) {
- return new Response(JSON.stringify(error.issues), { status: 422 })
- }
-
- return new Response(null, { status: 500 })
- }
-}
diff --git a/app/api/users/stripe/route.ts b/app/api/users/stripe/route.ts
deleted file mode 100644
index b6f0938..0000000
--- a/app/api/users/stripe/route.ts
+++ /dev/null
@@ -1,61 +0,0 @@
-import { getServerSession } from "next-auth/next"
-import { z } from "zod"
-
-import { proPlan } from "@/config/subscriptions"
-import { authOptions } from "@/lib/auth"
-import { stripe } from "@/lib/stripe"
-import { getUserSubscriptionPlan } from "@/lib/subscription"
-import { absoluteUrl } from "@/lib/utils"
-
-const billingUrl = absoluteUrl("/dashboard/billing")
-
-export async function GET(req: Request) {
- try {
- const session = await getServerSession(authOptions)
-
- if (!session?.user || !session?.user.email) {
- return new Response(null, { status: 403 })
- }
-
- const subscriptionPlan = await getUserSubscriptionPlan(session.user.id)
-
- // The user is on the pro plan.
- // Create a portal session to manage subscription.
- if (subscriptionPlan.isPro && subscriptionPlan.stripeCustomerId) {
- const stripeSession = await stripe.billingPortal.sessions.create({
- customer: subscriptionPlan.stripeCustomerId,
- return_url: billingUrl,
- })
-
- return new Response(JSON.stringify({ url: stripeSession.url }))
- }
-
- // The user is on the free plan.
- // Create a checkout session to upgrade.
- const stripeSession = await stripe.checkout.sessions.create({
- success_url: billingUrl,
- cancel_url: billingUrl,
- payment_method_types: ["card"],
- mode: "subscription",
- billing_address_collection: "auto",
- customer_email: session.user.email,
- line_items: [
- {
- price: proPlan.stripePriceId,
- quantity: 1,
- },
- ],
- metadata: {
- userId: session.user.id,
- },
- })
-
- return new Response(JSON.stringify({ url: stripeSession.url }))
- } catch (error) {
- if (error instanceof z.ZodError) {
- return new Response(JSON.stringify(error.issues), { status: 422 })
- }
-
- return new Response(null, { status: 500 })
- }
-}
diff --git a/app/api/webhooks/stripe/route.ts b/app/api/webhooks/stripe/route.ts
deleted file mode 100644
index 18c00fb..0000000
--- a/app/api/webhooks/stripe/route.ts
+++ /dev/null
@@ -1,70 +0,0 @@
-import { headers } from "next/headers"
-import Stripe from "stripe"
-
-import { db } from "@/lib/db"
-import { stripe } from "@/lib/stripe"
-
-export async function POST(req: Request) {
- const body = await req.text()
- const signature = headers().get("Stripe-Signature") as string
-
- let event: Stripe.Event
-
- try {
- event = stripe.webhooks.constructEvent(
- body,
- signature,
- process.env.STRIPE_WEBHOOK_SECRET || ""
- )
- } catch (error) {
- return new Response(`Webhook Error: ${error.message}`, { status: 400 })
- }
-
- const session = event.data.object as Stripe.Checkout.Session
-
- if (event.type === "checkout.session.completed") {
- // Retrieve the subscription details from Stripe.
- const subscription = await stripe.subscriptions.retrieve(
- session.subscription as string
- )
-
- // Update the user stripe into in our database.
- // Since this is the initial subscription, we need to update
- // the subscription id and customer id.
- await db.user.update({
- where: {
- id: session?.metadata?.userId,
- },
- data: {
- stripeSubscriptionId: subscription.id,
- stripeCustomerId: subscription.customer as string,
- stripePriceId: subscription.items.data[0].price.id,
- stripeCurrentPeriodEnd: new Date(
- subscription.current_period_end * 1000
- ),
- },
- })
- }
-
- if (event.type === "invoice.payment_succeeded") {
- // Retrieve the subscription details from Stripe.
- const subscription = await stripe.subscriptions.retrieve(
- session.subscription as string
- )
-
- // Update the price id and set the new period end.
- await db.user.update({
- where: {
- stripeSubscriptionId: subscription.id,
- },
- data: {
- stripePriceId: subscription.items.data[0].price.id,
- stripeCurrentPeriodEnd: new Date(
- subscription.current_period_end * 1000
- ),
- },
- })
- }
-
- return new Response(null, { status: 200 })
-}
diff --git a/app/layout.tsx b/app/layout.tsx
deleted file mode 100644
index 9ba740f..0000000
--- a/app/layout.tsx
+++ /dev/null
@@ -1,94 +0,0 @@
-import { Inter as FontSans } from "next/font/google"
-import localFont from "next/font/local"
-
-import "@/styles/globals.css"
-import { siteConfig } from "@/config/site"
-import { absoluteUrl, cn } from "@/lib/utils"
-import { Toaster } from "@/components/ui/toaster"
-import { Analytics } from "@/components/analytics"
-import { TailwindIndicator } from "@/components/tailwind-indicator"
-import { ThemeProvider } from "@/components/theme-provider"
-
-const fontSans = FontSans({
- subsets: ["latin"],
- variable: "--font-sans",
-})
-
-// Font files can be colocated inside of `pages`
-const fontHeading = localFont({
- src: "../assets/fonts/CalSans-SemiBold.woff2",
- variable: "--font-heading",
-})
-
-interface RootLayoutProps {
- children: React.ReactNode
-}
-
-export const metadata = {
- title: {
- default: siteConfig.name,
- template: `%s | ${siteConfig.name}`,
- },
- description: siteConfig.description,
- keywords: [
- "Next.js",
- "React",
- "Tailwind CSS",
- "Server Components",
- "Radix UI",
- ],
- authors: [
- {
- name: "shadcn",
- url: "https://shadcn.com",
- },
- ],
- creator: "shadcn",
- themeColor: [
- { media: "(prefers-color-scheme: light)", color: "white" },
- { media: "(prefers-color-scheme: dark)", color: "black" },
- ],
- openGraph: {
- type: "website",
- locale: "en_US",
- url: siteConfig.url,
- title: siteConfig.name,
- description: siteConfig.description,
- siteName: siteConfig.name,
- },
- twitter: {
- card: "summary_large_image",
- title: siteConfig.name,
- description: siteConfig.description,
- images: [`${siteConfig.url}/og.jpg`],
- creator: "@shadcn",
- },
- icons: {
- icon: "/favicon.ico",
- shortcut: "/favicon-16x16.png",
- apple: "/apple-touch-icon.png",
- },
- manifest: `${siteConfig.url}/site.webmanifest`,
-}
-
-export default function RootLayout({ children }: RootLayoutProps) {
- return (
-
-
-
-
- {children}
-
-
-
-
-
-
- )
-}
diff --git a/app/opengraph-image.jpg b/app/opengraph-image.jpg
deleted file mode 100644
index 75473bf..0000000
Binary files a/app/opengraph-image.jpg and /dev/null differ
diff --git a/app/robots.ts b/app/robots.ts
deleted file mode 100644
index f86f167..0000000
--- a/app/robots.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-import { MetadataRoute } from "next"
-
-export default function robots(): MetadataRoute.Robots {
- return {
- rules: {
- userAgent: "*",
- allow: "/",
- },
- }
-}