A modern learning management system built with Next.js. Dao LMS supports public course discovery, Stripe-powered enrollment, learner dashboards, lesson progress, and an admin workspace for managing courses, chapters, lessons, media, and analytics.
- Public course catalog with course detail pages
- Email and OAuth authentication with Better Auth
- Role-based admin and learner dashboards
- Course, chapter, and lesson management
- Drag-and-drop ordering for chapters and lessons
- Rich markdown lesson content with TipTap editing
- Markdown tables in the editor and renderer
- Video and image uploads through S3-compatible storage
- Stripe checkout and webhook-based enrollment
- Learner progress tracking
- Admin analytics with enrollment charts
- Next.js 16 and React 19
- TypeScript
- Tailwind CSS
- Drizzle ORM
- Neon Postgres
- Better Auth
- Stripe
- Tigris/S3-compatible object storage
- TipTap
- Recharts
- shadcn-style UI components
Install dependencies:
pnpm installCreate a .env file in the project root and fill in the required variables:
DATABASE_URL=
BETTER_AUTH_SECRET=
BETTER_AUTH_URL=
GITHUB_CLIENT_ID=
GITHUB_CLIENT_SECRET=
SMTP_USER=
SMTP_PASS=
SENDER_EMAIL=
TIGRIS_STORAGE_ACCESS_KEY_ID=
TIGRIS_STORAGE_SECRET_ACCESS_KEY=
TIGRIS_STORAGE_ENDPOINT=
TIGRIS_STORAGE_BUCKET=
NEXT_PUBLIC_TIGRIS_STORAGE_BUCKET=
STRIPE_SECRET_KEY=
STRIPE_PUBLISHABLE_KEY=
STRIPE_WEBHOOK_SECRET=
NEXT_PUBLIC_APP_URL=Push the database schema:
pnpm db:pushRun the development server:
pnpm devOpen http://localhost:3000.
pnpm dev # Start the local dev server
pnpm build # Build the production app
pnpm start # Start the production server
pnpm lint # Run ESLint
pnpm db:push # Push Drizzle schema changes
pnpm db:migrate # Run Drizzle migrations
pnpm db:generate # Generate Drizzle migration files
pnpm db:studio # Open Drizzle StudioEnrollments are written from Stripe webhook events after checkout succeeds. In local development, forward Stripe events to:
/api/webhooks/stripeThe webhook must send checkout.session.completed events and use the same signing secret stored in STRIPE_WEBHOOK_SECRET.
src/app App Router routes and API handlers
src/components Shared UI, dashboard, auth, and markdown components
src/db Drizzle database setup and schemas
src/features Feature modules for courses, chapters, lessons, enrollments
src/lib Auth, actions, utilities, and emails
src/services Stripe and storage integrations/adminshows analytics and recent courses./admin/coursesmanages course creation, editing, deletion, chapters, and lessons./dashboardshows enrolled and available courses for learners./dashboard/[courseId]/[lessonId]is the lesson player experience.
- Course purchases depend on valid Stripe price IDs created for each course.
- Uploaded media is stored in the configured S3-compatible bucket.
- Lesson descriptions are stored as markdown and rendered safely through the project markdown renderer.