A monorepo for the Deliverty platform - a peer-to-peer delivery service connecting people who need to send packages/documents with travelers who are already flying on the same route.
deliverty/
├── backend/ # Go backend API
├── frontend/ # React frontend
└── ops/ # Docker and deployment configuration
For local development, we use Docker Compose to run PostgreSQL locally.
Start PostgreSQL and Adminer using Docker Compose:
cd ops
docker compose up -dThis will:
- Start PostgreSQL on port
5433(mapped from container port 5432) - Start Adminer (database admin UI) on port
8081 - Automatically run migrations on first boot
Access Adminer at: http://localhost:8081
- System:
PostgreSQL - Server:
db - Username:
postgres - Password:
postgres - Database:
deliverty
Create a .env file in the ops/ directory:
cd ops
cat > .env << EOF
DATABASE_URL=postgres://postgres:postgres@localhost:5433/deliverty?sslmode=disable
HTTP_ADDR=:8080
TG_BOT_TOKEN=123456:ABC-your-bot-token
TG_BOT_NAME=deliverty_bot
TG_DEEPLINK_SECRET=choose-a-random-long-string
EOFNote: For local development, use port 5433 (Docker mapped port).
Get Telegram Bot Token:
- Talk to @BotFather on Telegram
- Send
/newbotand follow instructions - Copy the token (format:
123456:ABC-...) - Set
TG_BOT_NAMEto your bot's username (without @) - Choose a random string for
TG_DEEPLINK_SECRET
For production, use Supabase Postgres. Set the DATABASE_URL environment variable to your Supabase connection string:
DATABASE_URL=postgresql://postgres:[YOUR-PASSWORD]@[YOUR-PROJECT].supabase.co:5432/postgres?sslmode=requireThe application automatically uses the DATABASE_URL environment variable - no code changes needed!
Run the backend:
export $(cat ops/.env | xargs)
go run ./backend/cmd/apiAfter the backend is running, configure the Telegram webhook (replace DOMAIN with your backend URL):
BOT=your-bot-token-here
curl -X POST "https://api.telegram.org/bot$BOT/setWebhook" \
-H "Content-Type: application/json" \
-d '{"url":"https://DOMAIN/bot/webhook"}'For local development with ngrok:
# Install ngrok, then:
ngrok http 8080
# Use the https URL provided by ngrok in the webhook:
curl -X POST "https://api.telegram.org/bot$BOT/setWebhook" \
-H "Content-Type: application/json" \
-d '{"url":"https://your-ngrok-url.ngrok.io/bot/webhook"}'Copy environment template and configure:
cp frontend/.env.example frontend/.envEdit frontend/.env:
VITE_API_BASE=http://localhost:8080/api
VITE_TG_BOT=deliverty_botInstall dependencies and run:
cd frontend
npm install
npm run devFrontend will be available at: http://localhost:5173
The app uses Telegram Login Widget for authentication (no need to host as Mini App):
-
Create Bot (if not done already):
- Talk to @BotFather
- Send
/newbotand follow instructions - Get your bot token and username
-
Set Domain for Login Widget:
For Production:
- Send
/setdomainto @BotFather - Select your bot
- Provide your frontend URL (where Login Widget is hosted)
- If using Vercel:
your-project.vercel.app(or your custom domain) - If using other hosting: your frontend domain (e.g.,
your-domain.com)
- If using Vercel:
⚠️ Important: Use your frontend URL (Vercel), NOT backend URL (Render)- This is required for the Login Widget to work in production
For Local Development:
- Option A: Use a tunnel service (ngrok, localtunnel, etc.)
# Install ngrok, then: ngrok http 5173 # or your frontend port # Use the ngrok URL (e.g., https://abc123.ngrok.io) in /setdomain
- Option B: Skip auth setup and use fallback user ID
- The app will use
123456789as fallback in dev mode - No Telegram auth needed for local testing
- The app will use
- Send
-
Configure Frontend:
- Set
VITE_TG_BOTinfrontend/.envto your bot username (without @) - Example:
VITE_TG_BOT=deliverty_bot
- Set
-
User Registration:
- Users visit
/authpage and click "Login with Telegram" - They authorize via Telegram Login Widget
- User ID is saved to localStorage and user is created in database
- No manual registration needed!
- Users visit
Important:
- Domain is required for production use
- For local dev, you can use tunnels (ngrok) or skip auth entirely (fallback mode)
- The Login Widget will only work on the domain you set via
/setdomain
Test the API endpoints:
# Health check
curl http://localhost:8080/healthz
# Airport search
curl "http://localhost:8080/api/airports?q=bkk"Run the end-to-end QA test script:
cd ops
./qa_test.shOr run individual test commands manually using curl against the endpoints listed above.
Run Order:
- Database:
cd ops && docker compose up -d - Backend: Set env vars, then
go run ./backend/cmd/api - Frontend:
cd frontend && npm run dev
API Endpoints:
GET /healthz- Health checkGET /api/airports?q=search- Airport searchGET /api/auth/telegram- Telegram Login Widget callbackPOST /api/publications- Create publication (requiresX-TG-User-IDheader)GET /api/publications?from=BKK&to=SVO- List publicationsGET /api/matches?pub_id=123- Find matchesPOST /api/deals- Create dealPOST /bot/webhook- Telegram bot webhook
- Publications: Create requests (need to send something) or trips (flying and can take something along)
- Matching: Automatic matching based on route, dates, and weight compatibility
- Deals: Connect senders and travelers via secure Telegram relay
- Ratings: Simple rating system for completed deals
- Reminders: Automatic pre-flight reminders (24h and 3h before)
- Content Policy: Banned items filtering and description validation
- Rate Limiting: IP-based rate limiting to prevent abuse
- Anti-Spam: Duplicate publication detection
- Telegram Authentication: Login via Telegram Login Widget (OAuth-like, no Mini App required)
Production uses Supabase Postgres - set the DATABASE_URL environment variable to your Supabase connection string. The application automatically uses this variable - no code changes needed!
See ops/ directory for:
nginx.conf.example- Nginx reverse proxy configurationdeliverty-api.service.example- Systemd service file (setDATABASE_URLto your Supabase URL)deploy.sh- Automated build and deployment script
Quick deploy:
./ops/deploy.sh user@your-server.comImportant: Make sure to set DATABASE_URL environment variable in your production environment to your Supabase Postgres connection string.
Import airport data from CSV:
# For local development (Docker):
go run ./backend/cmd/airports-load \
"postgres://postgres:postgres@localhost:5433/deliverty?sslmode=disable" \
./ops/airports.csv
# For production (Supabase):
go run ./backend/cmd/airports-load \
"$DATABASE_URL" \
./ops/airports.csvMIT