A modern, full-stack equipment booking and management platform (and more) built for clubs and organizations.
- Equipment Catalog: Browse available equipment with real-time availability status
- Smart Booking System: Book equipment with time slot selection and conflict detection
- Google Calendar Integration: View equipment availability directly from Google Calendar
- Telegram Bot: Receive notifications, check status, and manage bookings via Telegram
- Pickup Confirmation: 30-minute window to confirm equipment pickup
- Partial Returns: Return some items while keeping others with automatic grace periods
- Photo Albums: Upload and browse club photo galleries backed by Google Drive
- Booking History: Track all your past and current bookings
- Dashboard: Overview of bookings, equipment usage, and system statistics
- Equipment Management: Add, edit, and manage equipment with categories and clearance levels
- User Management: Control user access, roles, and clearance levels
- Booking Oversight: Monitor all bookings, handle overdue equipment, and manage conflicts
- Album Management: Oversee public and member albums
- Settings: Configure operating hours, booking limits, and global notifications
- Clearance Levels: Restrict high-end equipment to authorized users
- Overdue Tracking: Automatic alerts for late returns
- Photo Verification: Timestamped photos required for equipment returns
- Multi-item Bookings: Book multiple pieces of equipment with independent time slots
- Booking Extensions: Extend bookings by 30 minutes if no conflicts exist
- Frontend: React 19, TanStack Start, TanStack Router, TanStack Query
- UI: Tailwind CSS v4, shadcn/ui, Radix UI
- Backend: Cloudflare Workers (serverless)
- Database: Cloudflare D1 (SQLite), Drizzle ORM
- Storage: Cloudflare R2 (object storage), Cloudflare KV (caching)
- Authentication: Better Auth with Google OAuth
- Calendar: Google Calendar API (OAuth refresh tokens)
- Notifications: Telegram Bot API
- Build: Vite
- Deployment: Cloudflare Workers
- Node.js 18+ and npm
- Cloudflare account
- Google Cloud project with Calendar API enabled
- Telegram Bot Token
# Install dependencies
npm install
# Set up environment variables
cp .env.example .env
# Edit .env with your credentials
# Generate database migrations
npm run db:generate
# Apply migrations locally
npm run db:migrate-local
# Start development server
npm run devThe app will be available at http://localhost:3000
Create a .env file with the following:
# Better Auth
BETTER_AUTH_SECRET=your_secret_key
BETTER_AUTH_URL=http://localhost:3000
# Cloudflare (for local dev — production uses wrangler bindings)
CLOUDFLARE_ACCOUNT_ID=
CLOUDFLARE_DATABASE_ID=
CLOUDFLARE_D1_TOKEN=
# Google OAuth (Calendar + Drive)
GOOGLE_CLIENT_ID=your_google_client_id
GOOGLE_CLIENT_SECRET=your_google_client_secret
GOOGLE_MASTER_ACCESS_TOKEN=your_access_token
GOOGLE_MASTER_REFRESH_TOKEN=your_refresh_token
# Telegram Bot
TELEGRAM_BOT_TOKEN=your_telegram_bot_token
TELEGRAM_CLUB_CHANNEL_ID=your_channel_id
TELEGRAM_WEBHOOK_SECRET=your_webhook_secret
TELEGRAM_BOT_USERNAME=your_bot_username
TELEGRAM_RETURN_ENABLED=true
# Development mode (auto-skips Telegram onboarding step)
DEV=true# Start development server (with Wrangler)
npm run dev
# Start Vite dev server only
npm run dev:vite
# Run tests
npm run test
# Lint code
npm run lint
# Format code
npm run format# Promote your local user to admin for testing admin-only flows.
npx wrangler d1 execute meriksirat_d1 --local --command "update user set role='admin' where email='you@example.com';"# Generate new migration
npm run db:generate
# Apply migrations locally
npm run db:migrate-local
# Apply migrations to production
npm run db:migrate-remote# Build and deploy to Cloudflare Workers
npm run deployThe app uses the following Cloudflare resources:
- D1 Database:
meriksirat_d1— stores all application data - R2 Bucket:
meriksirat— stores equipment images and return photos - KV Namespace:
meriksirat_kv— caches album listing data - Cron Triggers: Runs every 5 minutes to check booking statuses
src/
├── routes/ # File-based router (TanStack). Thin page constructors.
│ ├── _public/ # Public routes (login, signup)
│ └── _authenticated/ # Authed area: equipment, bookings, albums, admin/*
├── components/
│ ├── ui/ # shadcn/ui primitives
│ ├── shared/ # Cross-page components (faceted filter, dialogs, calendar)
│ ├── layout/ # PageContainer, PageHeader, sidebar shell
│ ├── root/ # SiteHeader, authenticated shell
│ ├── admin/ # Admin pages (mirror routes)
│ ├── albums/ # Album pages
│ ├── bookings/ # Booking pages
│ ├── equipment/ # Equipment pages
│ ├── onboarding/ # Onboarding flow
│ └── profile/ # User profile
├── lib/
│ ├── admin/ # Admin server functions + queries
│ ├── albums/ # Album server functions + upload manager
│ ├── auth/ # Better Auth setup + onboarding
│ ├── booking/ # Server functions, queries, types
│ ├── equipment/ # Server functions, queries, types
│ ├── user/ # User admin queries
│ ├── google/ # Google Calendar + Drive integration
│ ├── telegram/ # Telegram bot commands + logging
│ ├── search-params.ts # URL array-param zod helpers
│ └── query-client.ts # SSR-aware QueryClient factory
├── db/ # Database schema and migrations
└── router.tsx # Router + SSR query integration
- Browse equipment catalog
- Select equipment and view calendar availability
- Choose date and time slots (30-minute increments)
- Confirm booking with optional notes
- Receive Telegram notification
- Confirm pickup within 15 minutes of start time
- Open Telegram bot
- Select "End Booking" from the menu
- Choose the booking and items to return
- Send a photo of the equipment
- Photo and return logged to the club channel
The bot is menu-driven (inline keyboard, no slash commands):
- My Bookings — view active and upcoming bookings with item statuses
- Start Booking — confirm pickup for bookings in the start window
- End Booking — return equipment by selecting items and sending a photo
- Cancel Booking — cancel bookings that haven't been picked up yet
Reminders, overdue alerts, and admin notifications are sent as separate
messages. See docs/dev/logs.md for the full list of message formats.
Detailed documentation lives in docs/:
- FAQ — common questions (linked from sidebar)
- Terms of Service — user agreement (linked from sidebar + onboarding)
- Architecture — stack overview and directory layout
- Conventions — route/component patterns, code style
- Data Loading — TanStack Query + SSR integration
- Albums — Drive-backed photo galleries and upload system
- Calendar Viewer — custom calendar replacing iframe embeds
- Availability Badges — Google Calendar free/busy batching
- Worker Import Convention — server-only import rules
- Telegram Logs — inventory of all Telegram message formats
- shadcn Reference — component index
- Starter Prompt — AI session bootstrap
- Booking Flow — full booking lifecycle
- Telegram Bot — bot usage guide
- Member Guide — how to use the platform
- Admin Guide — administrative operations
This is a private project for club use. For feature requests or bug reports, contact the administrators.
GNU Affero General Public License v3.0 — see LICENSE for details.