From 8d17bd80c5f9ff2d889c8070ab2eab05fc81a674 Mon Sep 17 00:00:00 2001 From: javayhu Date: Fri, 4 Jul 2025 23:23:25 +0800 Subject: [PATCH 1/5] feat: implement admin access control in UsersLayout --- src/app/[locale]/(protected)/admin/users/layout.tsx | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/app/[locale]/(protected)/admin/users/layout.tsx b/src/app/[locale]/(protected)/admin/users/layout.tsx index 5eb394e..1bd220c 100644 --- a/src/app/[locale]/(protected)/admin/users/layout.tsx +++ b/src/app/[locale]/(protected)/admin/users/layout.tsx @@ -1,11 +1,21 @@ import { DashboardHeader } from '@/components/dashboard/dashboard-header'; +import { getSession } from '@/lib/server'; import { getTranslations } from 'next-intl/server'; +import { notFound } from 'next/navigation'; interface UsersLayoutProps { children: React.ReactNode; } export default async function UsersLayout({ children }: UsersLayoutProps) { + // if is demo website, allow user to access admin and user pages, but data is fake + const isDemo = process.env.NEXT_PUBLIC_DEMO_WEBSITE === 'true'; + // Check if user is admin + const session = await getSession(); + if (!session || (session.user.role !== 'admin' && !isDemo)) { + notFound(); + } + const t = await getTranslations('Dashboard.admin'); const breadcrumbs = [ From 8657bf4e843775f19511a0e2af104c17a1b185e4 Mon Sep 17 00:00:00 2001 From: javayhu Date: Sat, 5 Jul 2025 10:10:39 +0800 Subject: [PATCH 2/5] chore: show theme selector in demo website only --- src/components/dashboard/dashboard-header.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/components/dashboard/dashboard-header.tsx b/src/components/dashboard/dashboard-header.tsx index 562c406..22e514e 100644 --- a/src/components/dashboard/dashboard-header.tsx +++ b/src/components/dashboard/dashboard-header.tsx @@ -29,6 +29,9 @@ export function DashboardHeader({ breadcrumbs, actions, }: DashboardHeaderProps) { + // if is demo website, allow user to access admin and user pages, but data is fake + const isDemo = process.env.NEXT_PUBLIC_DEMO_WEBSITE === 'true'; + return (
@@ -69,7 +72,7 @@ export function DashboardHeader({
{actions} - + {isDemo && }
From 6980507c431bd665e1f334402137267766974c96 Mon Sep 17 00:00:00 2001 From: javayhu Date: Sat, 5 Jul 2025 15:51:36 +0800 Subject: [PATCH 3/5] chore: update dashboard and setting pages layout --- .../(protected)/settings/billing/layout.tsx | 26 +++++++++++-------- .../settings/notifications/layout.tsx | 26 +++++++++++-------- .../settings/notifications/page.tsx | 2 +- .../(protected)/settings/profile/layout.tsx | 26 +++++++++++-------- .../(protected)/settings/profile/page.tsx | 2 +- .../(protected)/settings/security/layout.tsx | 26 +++++++++++-------- .../(protected)/settings/security/page.tsx | 2 +- 7 files changed, 63 insertions(+), 47 deletions(-) diff --git a/src/app/[locale]/(protected)/settings/billing/layout.tsx b/src/app/[locale]/(protected)/settings/billing/layout.tsx index 3cd81bd..af239d4 100644 --- a/src/app/[locale]/(protected)/settings/billing/layout.tsx +++ b/src/app/[locale]/(protected)/settings/billing/layout.tsx @@ -23,18 +23,22 @@ export default async function BillingLayout({ children }: BillingLayoutProps) { <> -
-
-
-

- {t('billing.title')} -

-

- {t('billing.description')} -

-
+
+
+
+
+
+

+ {t('billing.title')} +

+

+ {t('billing.description')} +

+
- {children} + {children} +
+
diff --git a/src/app/[locale]/(protected)/settings/notifications/layout.tsx b/src/app/[locale]/(protected)/settings/notifications/layout.tsx index b29fc78..e480a3a 100644 --- a/src/app/[locale]/(protected)/settings/notifications/layout.tsx +++ b/src/app/[locale]/(protected)/settings/notifications/layout.tsx @@ -25,18 +25,22 @@ export default async function NotificationsLayout({ <> -
-
-
-

- {t('notification.title')} -

-

- {t('notification.description')} -

-
+
+
+
+
+
+

+ {t('notification.title')} +

+

+ {t('notification.description')} +

+
- {children} + {children} +
+
diff --git a/src/app/[locale]/(protected)/settings/notifications/page.tsx b/src/app/[locale]/(protected)/settings/notifications/page.tsx index 9d48541..18d2ddb 100644 --- a/src/app/[locale]/(protected)/settings/notifications/page.tsx +++ b/src/app/[locale]/(protected)/settings/notifications/page.tsx @@ -2,7 +2,7 @@ import { NewsletterFormCard } from '@/components/settings/notification/newslette export default function NotificationPage() { return ( -
+
); diff --git a/src/app/[locale]/(protected)/settings/profile/layout.tsx b/src/app/[locale]/(protected)/settings/profile/layout.tsx index 327cbdd..c807a00 100644 --- a/src/app/[locale]/(protected)/settings/profile/layout.tsx +++ b/src/app/[locale]/(protected)/settings/profile/layout.tsx @@ -23,18 +23,22 @@ export default async function ProfileLayout({ children }: ProfileLayoutProps) { <> -
-
-
-

- {t('profile.title')} -

-

- {t('profile.description')} -

-
+
+
+
+
+
+

+ {t('profile.title')} +

+

+ {t('profile.description')} +

+
- {children} + {children} +
+
diff --git a/src/app/[locale]/(protected)/settings/profile/page.tsx b/src/app/[locale]/(protected)/settings/profile/page.tsx index b3633df..f6915df 100644 --- a/src/app/[locale]/(protected)/settings/profile/page.tsx +++ b/src/app/[locale]/(protected)/settings/profile/page.tsx @@ -3,7 +3,7 @@ import { UpdateNameCard } from '@/components/settings/profile/update-name-card'; export default function ProfilePage() { return ( -
+
diff --git a/src/app/[locale]/(protected)/settings/security/layout.tsx b/src/app/[locale]/(protected)/settings/security/layout.tsx index 320d4a0..d42ac2e 100644 --- a/src/app/[locale]/(protected)/settings/security/layout.tsx +++ b/src/app/[locale]/(protected)/settings/security/layout.tsx @@ -25,18 +25,22 @@ export default async function SecurityLayout({ <> -
-
-
-

- {t('security.title')} -

-

- {t('security.description')} -

-
+
+
+
+
+
+

+ {t('security.title')} +

+

+ {t('security.description')} +

+
- {children} + {children} +
+
diff --git a/src/app/[locale]/(protected)/settings/security/page.tsx b/src/app/[locale]/(protected)/settings/security/page.tsx index 1262d53..7574f3c 100644 --- a/src/app/[locale]/(protected)/settings/security/page.tsx +++ b/src/app/[locale]/(protected)/settings/security/page.tsx @@ -3,7 +3,7 @@ import { PasswordCardWrapper } from '@/components/settings/security/password-car export default function SecurityPage() { return ( -
+
From 8a9c76c6285820f210bc0261687f7d56e7da8b14 Mon Sep 17 00:00:00 2001 From: javayhu Date: Sat, 5 Jul 2025 17:11:07 +0800 Subject: [PATCH 4/5] fix: add missing newline at end of index.zh.mdx file --- content/docs/components/index.zh.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/docs/components/index.zh.mdx b/content/docs/components/index.zh.mdx index e49d3de..755182a 100644 --- a/content/docs/components/index.zh.mdx +++ b/content/docs/components/index.zh.mdx @@ -2,4 +2,4 @@ title: 组件 description: 改进文档的额外组件 index: true ---- \ No newline at end of file +--- From 0d4e8fe899fdefe503f4122940680a23c07f1189 Mon Sep 17 00:00:00 2001 From: javayhu Date: Tue, 8 Jul 2025 00:30:40 +0800 Subject: [PATCH 5/5] docs: add CLAUDE.md for project guidance --- CLAUDE.md | 109 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..3663f43 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,109 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Development Commands + +### Core Development +- `pnpm dev` - Start development server with content collections +- `pnpm build` - Build the application and content collections +- `pnpm start` - Start production server +- `pnpm lint` - Run Biome linter (use for code quality checks) +- `pnpm format` - Format code with Biome + +### Database Operations (Drizzle ORM) +- `pnpm db:generate` - Generate new migration files based on schema changes +- `pnpm db:migrate` - Apply pending migrations to the database +- `pnpm db:push` - Sync schema changes directly to the database (development only) +- `pnpm db:studio` - Open Drizzle Studio for database inspection and management + +### Content and Email +- `pnpm content` - Process MDX content collections +- `pnpm email` - Start email template development server on port 3333 + +## Project Architecture + +This is a Next.js 15 full-stack SaaS application with the following key architectural components: + +### Core Stack +- **Framework**: Next.js 15 with App Router +- **Database**: PostgreSQL with Drizzle ORM +- **Authentication**: Better Auth with social providers (Google, GitHub) +- **Payments**: Stripe integration with subscription and one-time payments +- **UI**: Radix UI components with TailwindCSS +- **State Management**: Zustand for client-side state +- **Internationalization**: next-intl with English and Chinese locales +- **Content**: Fumadocs for documentation and MDX for content +- **Code Quality**: Biome for formatting and linting + +### Key Directory Structure +- `src/app/` - Next.js app router with internationalized routing +- `src/components/` - Reusable React components organized by feature +- `src/lib/` - Utility functions and shared code +- `src/db/` - Database schema and migrations +- `src/actions/` - Server actions for API operations +- `src/stores/` - Zustand state management +- `src/hooks/` - Custom React hooks +- `src/config/` - Application configuration files +- `src/i18n/` - Internationalization setup +- `src/mail/` - Email templates and mail functionality +- `src/payment/` - Stripe payment integration +- `src/credits/` - Credit system implementation +- `content/` - MDX content files for docs and blog +- `messages/` - Translation files (en.json, zh.json) for internationalization + +### Authentication & User Management +- Uses Better Auth with PostgreSQL adapter +- Supports email/password and social login (Google, GitHub) +- Includes user management, email verification, and password reset +- Admin plugin for user management and banning +- Automatic newsletter subscription on user creation + +### Payment System +- Stripe integration for subscriptions and one-time payments +- Three pricing tiers: Free, Pro (monthly/yearly), and Lifetime +- Credit system with packages for pay-per-use features +- Customer portal for subscription management + +### Feature Modules +- **Blog**: MDX-based blog with pagination and categories +- **Docs**: Fumadocs-powered documentation +- **AI Features**: Image generation with multiple providers (OpenAI, Replicate, etc.) +- **Newsletter**: Email subscription system +- **Analytics**: Multiple analytics providers support +- **Storage**: S3 integration for file uploads + +### Development Workflow +1. Use TypeScript for all new code +2. Follow Biome formatting rules (single quotes, trailing commas) +3. Write server actions in `src/actions/` +4. Use Zustand for client-side state management +5. Implement database changes through Drizzle migrations +6. Use Radix UI components for consistent UI +7. Follow the established directory structure +8. Use proper error handling with error.tsx and not-found.tsx +9. Leverage Next.js 15 features like Server Actions +10. Use `next-safe-action` for secure form submissions + +### Configuration +- Main config in `src/config/website.tsx` +- Environment variables template in `env.example` +- Database config in `drizzle.config.ts` +- Biome config in `biome.json` with specific ignore patterns +- TypeScript config with path aliases (@/* for src/*) + +### Testing and Quality +- Use Biome for linting and formatting +- TypeScript for type safety +- Environment variables for configuration +- Proper error boundaries and not-found pages +- Zod for runtime validation + +## Important Notes + +- The project uses pnpm as the package manager +- Database schema is in `src/db/schema.ts` with auth, payment, and credit tables +- Email templates are in `src/mail/templates/` +- The app supports both light and dark themes +- Content is managed through MDX files in the `content/` directory +- The project includes comprehensive internationalization support