A comprehensive course and event booking application with a full CRUD API. This platform allows users to browse and book available courses and events, while providing an admin section for efficient management of all aspects, including users, courses, sessions, and lessons.
The application is composed of a server-side API built with Nuxt and a client-side interface, also built with Nuxt, offering a seamless and integrated user experience.
- RESTful API: Built with Nuxt, providing robust CRUD operations for all entities.
- Database Integration: Utilizes a SQLite database with schema managed by Drizzle ORM.
- Authentication & Authorization: Secure user authentication via Azure AD and role-based access control for admin functionalities.
- Automated Migrations: Database migrations are automatically run on server startup using the
server/plugins/migrateDb.tsplugin. - ** Services**: Transactional emails (registration confirmations, cancellations, custom notifications) rendered via Handlebars templates in
server/templates/. - ICS Calendar Support: Generates and versions iCalendar files for session bookings.
- OpenAPI Documentation: Auto-generated API docs available at
/_openapi.json,/_scalar, and/_swagger.
The client-side application is the user-facing front-end of the Coursebook platform, built as a Single Page Application (SPA) using Nuxt 4, Vue 3, and TypeScript. It is responsible for presenting data, handling user interactions, managing authentication state, and providing an intuitive interface.
- Nuxt 4: The meta-framework for Vue.js, used for its powerful features like file-based routing, server-side rendering (SSR) capabilities, and a rich module ecosystem.
- Vue 3: The progressive JavaScript framework for building user interfaces.
- TypeScript: A typed superset of JavaScript that compiles to plain JavaScript, enhancing code quality and maintainability.
- TailwindCSS &
@nuxt/uiv4: For styling and UI components, providing a consistent and customizable design system. motion-v: For creating smooth animations and transitions within the application.@formkit/nuxt: For building powerful, accessible, and schema-driven forms.@nuxtjs/i18n: For internationalization, allowing the application to support multiple languages.@sidebase/nuxt-auth: For handling authentication, integrated with Azure AD.@nuxtjs/mdc: For rendering Markdown content components.@dcc-bs/*libraries: Shared packages for authentication, common UI components, dependency injection, feedback control, and logging.
The client-side application communicates with the backend API (also part of the Nuxt project) to fetch and manipulate data, ensuring a seamless full-stack experience.
The app/ directory contains all the client-side code, organized to promote modularity and maintainability.
components/: Reusable Vue components.admin/: Components for the admin dashboard:AdminCourseCard.vue,AdminCourseForm.vue— Course management.AdminSessionCard.vue,AdminSessionForm.vue— Session management.AdminLessonCard.vue,AdminLessonForm.vue— Lesson management.AdminUserCard.vue,AdminUserForm.vue— User management.AdminRegistrationsCard.vue— Registration overview.AdminHeader.vue— Admin navigation header.FormEditor.vue— Custom registration form editor.NotifyConfirmModal.vue— Confirmation modal for session notifications.
- Shared Components:
DateTime.vue,ErrorView.vue,LoadingView.vue,NavigationMenu.vue,SessionView.vue(handles registration/unregistration).
composables/: Vue composables for state management and logic reuse (courses.composable.ts,sessions.composable.ts,me.composable.ts,users.composable.ts,useApiFetch.composable.ts,userFeedback.ts).layouts/: Different page structures.default.vue: Standard layout for public pages.admin.vue: Admin layout with role-based access control (checksisAdmin).auth.vue: Layout for authentication pages.
pages/: File-based routing.- Public/User-Facing Pages:
index.vue: Homepage with course listing, search, and filtering.courses/[id].vue: Course detail page with session information.courses/[courseId]/[sessionId].vue: Session detail page.me.vue: User profile/dashboard showing registrations and upcoming sessions.
- Auth Pages:
auth/signIn.vue: Sign-in page.
- Admin Pages (
/admin/):index.vue: Admin dashboard with statistics and quick actions.users.vue: User management.courses/: Course management (create, edit, list).courses/[id]/sessions/: Session management for a course.courses/[id]/sessions/[sessionId]/notify.vue: Send notifications to registered users.
- Public/User-Facing Pages:
types/: TypeScript type definitions.utils/: Client-side utility functions (e.g.,course.utils.ts,dateFormat.utils.ts).
- User Interface (UI):
- Design System: Built using
@nuxt/uiv4 components styled with TailwindCSS. - Responsive Design: Fully responsive for an optimal experience on all devices.
- Animations: Uses
motion-vfor smooth UI animations.
- Design System: Built using
- Internationalization (i18n):
- Setup: Handled by
@nuxtjs/i18n. - Locales: Translation files in
i18n/locales/(en.json,de.json). Default is German. - Usage:
useI18n()composable for translations.
- Setup: Handled by
- Authentication & Authorization:
- Authentication: Managed by
@sidebase/nuxt-authwith Azure AD. - Authorization: Client-side route protection in
admin.vuelayout. - User State:
useMecomposable for managing authenticated user's state.
- Authentication: Managed by
- Data Fetching & State Management:
- Composables: Data fetching logic encapsulated in composables (
useCourses,useSessions,useMe,useUsers). - State: Managed via reactive references (
ref,computed) and Nuxt'suseState. - API Interaction:
useApiFetchcomposable or directuseFetch/$fetchfor API calls.
- Composables: Data fetching logic encapsulated in composables (
- Forms:
- Form Building: Using
@formkit/nuxtand@nuxt/ui. - Validation: Zod schemas for form data validation.
- Input Masking:
maskafor formatted input fields. - Dynamic Forms:
FormEditor.vueallows admins to create custom JSON schema-based registration forms.
- Form Building: Using
- Navigation:
- Component:
NavigationMenu.vuefor primary links. - Routing: Nuxt's file-based routing. Programmatic navigation with
useRouter()andnavigateTo().
- Component:
- Discovery: User lands on
index.vue, views and filters/searches courses. - Course Details: User clicks a course to view
courses/[id].vue, seeing full details and sessions. - Session Selection: User reviews sessions on the course page.
- Registration: User clicks "Register" in
SessionView.vue.- If a custom form exists, it's rendered in a drawer for completion.
- Otherwise, registration is immediate.
- Confirmation: API call registers the user. Success message is shown, and UI updates.
- Calendar: User can download an ICS file for the booked session.
- Login: Admin logs in with Azure AD credentials.
- Admin Dashboard: Admin is redirected to
/admin/index.vue. - Course Management: Admin navigates to
/admin/coursesto list courses. - Create/Edit Course:
- Create: Admin goes to
/admin/courses/create, fills outAdminCourseForm.vue, and submits to create via API. - Edit: Admin clicks "Edit" on a course, pre-fills
AdminCourseForm.vueon/admin/courses/[id]/edit, and submits to update via API.
- Create: Admin goes to
- Session & Lesson Management: Admin manages sessions and lessons for a course through form-based interfaces under
/admin/courses/[id]/sessions/. - Notifications: Admin can send custom notifications to registered users from
/admin/courses/[id]/sessions/[sessionId]/notify.
- Bun (recommended) or Node.js
- A running SQLite database (or use the default local file-based DB)
bun installCopy the .env.schema file to .env and fill in the required values. The project uses Varlock with the ProtonPass plugin for secret management.
| Variable | Required | Description |
|---|---|---|
DATABASE_URL |
Yes | SQLite database path (e.g., data/coursebooker.db) |
NUXT_AUTH_SECRET |
Yes | Secret key for authentication |
AZURE_AD_TENANT_ID |
Yes | Azure AD tenant ID |
AZURE_AD_CLIENT_ID |
Yes | Azure AD client ID |
AZURE_AD_CLIENT_SECRET |
Yes | Azure AD client secret |
AUTH_ORIGIN |
No | Auth origin URL |
SMTP_HOST |
No | SMTP server host (default: localhost) |
SMTP_PORT |
No | SMTP server port (default: 1030) |
GITHUB_TOKEN |
No | GitHub token for feedback control integration |
MAIL_FROM |
No | Sender email address for outgoing mails (default: noreply@example.com) |
DEFAULT_ADMIN |
No | Default admin user email (build time) |
NUXT_DEFAULT_ADMIN |
No | Default admin user email (runtime, overrides DEFAULT_ADMIN) |
NUXT_SITE_URL |
No | Site URL (default: http://localhost:3000) |
LOG_LEVEL |
No | Log level: trace, debug, info, warn, error, fatal |
bun run devOn initial setup and after running database migrations, a default admin user is automatically created to facilitate access to the admin panel. Set the DEFAULT_ADMIN (at build time) or NUXT_DEFAULT_ADMIN (at runtime) environment variable to a valid email that exists in the Entra organization.
- Make changes to the database schema in
shared/schema/. - Run
bun run db:generateto create a new migration file. - The migration file will be created in the
drizzle/folder.
The server/plugins/migrateDb.ts plugin will automatically run the migrations on server start.
Production:
docker compose up -dThe app is exposed on port 8502 (mapped to internal port 3000). Data is persisted in the ./data volume.
Development (mail testing):
bun run docker:dev- To test email sending locally: Run
bun run docker:devand open http://localhost:1080 to access the mail testing interface.
- To open Drizzle Studio for database inspection and querying, run
bun run db:studioand navigate to https://local.drizzle.studio.
| Script | Description |
|---|---|
bun run dev |
Start development server |
bun run build |
Build for production |
bun run start |
Start production server |
bun run lint |
Format code with Biome |
bun run check |
Lint and format with Biome |
bun run db:generate |
Generate a new migration |
bun run db:push |
Push schema changes directly to DB |
bun run db:studio |
Open Drizzle Studio |
bun run db:migrate |
Run migrations manually |
bun run env:check |
Validate environment variables |
bun run docker:dev |
Start mail dev container |
bun run docker:dev:down |
Stop mail dev container |
See ./server/README.md for detailed API documentation. Interactive API docs are also available at /_scalar and /_swagger when the server is running.