Ibtikar Auto Detect is a dual-platform attendance system for Ibtikar Innovation Hub events:
| Platform | Description |
|---|---|
| Web (Cloudflare Worker) | Volunteer portal — webcam QR scanning or manual lookup, marks attendance & confirms payment |
| Raspberry Pi 4 / Laptop | Automatic QR detection via camera, marks attendance without volunteer interaction |
QR code format: membershipNumber:email:eventId
Example: 2302002:member@example.com:b1ed6cdc-d9a9-4f08-ac48-54fe3eb52207
Camera reads QR code
│
▼
Search Google Sheet (Form Responses 1)
│
Found? ─── YES ──► Update column M (attendance) or O (payment)
│
NO
│
▼
Search VMS (vms.ibtikar.tr)
│
Found? ─── YES ──► POST /event-registrations/{id}/approve?type=attendance
│
NO ──► Log "not found"
| URL | Purpose |
|---|---|
| https://auto-detect.ibtikar.tr | Primary (custom domain) |
| https://ibtikar-auto-detect.ibtikar-org.workers.dev | Backup (workers.dev) |
| Endpoint | What it shows |
|---|---|
/api/health |
Confirms all secrets are loaded |
/api/debug |
Live test: Google Sheets + VMS connectivity |
qr-code-reader/
├── web/ # Hono + Cloudflare Workers (TypeScript)
│ ├── src/
│ │ ├── index.ts # Main app — all routes & API
│ │ ├── types.ts # Shared TypeScript interfaces
│ │ ├── services/
│ │ │ ├── auth.ts # Session cookie (HMAC-SHA256)
│ │ │ ├── sheets.ts # Google Sheets API (JWT auth, no SDK)
│ │ │ └── vms.ts # VMS API client
│ │ └── views/
│ │ ├── login.ts # Login page HTML
│ │ └── app.ts # Scanner app HTML
│ ├── wrangler.jsonc # Cloudflare Workers config
│ ├── package.json
│ └── tsconfig.json
│
├── pi/ # Python — Raspberry Pi 4 / Laptop
│ ├── scanner.py # Main camera loop
│ ├── services/
│ │ ├── sheets.py # Google Sheets (gspread)
│ │ └── vms.py # VMS API client
│ ├── requirements.txt
│ └── README.md # Pi-specific setup instructions
│
├── .github/
│ └── workflows/
│ └── deploy-web.yml # Auto-deploy on push to main
│
├── example-qr-code/
│ └── image.png # Sample QR: 2302002:eng.abduallah1@gmail.com:eventId
│
└── .env.example # Template for required environment variables
Copy .env.example to .env. Never commit .env to git.
| Variable | Description | Example |
|---|---|---|
GOOGLE_API_KEY |
Full service account JSON (single line) | {"type":"service_account",...} |
ETC_GOOGLE_SHEET_ID |
Google Spreadsheet ID (from URL) | 1V9U_0eeMlba559n... |
GOOGLE_SHEET_NAME |
Sheet tab name | Form Responses 1 |
ETC_2026_EVENT_ID |
VMS Event UUID | b1ed6cdc-d9a9-4f08-... |
VMS_LOGIN |
VMS login (membership number) | 2302002 |
VMS_PASSWORD |
VMS password | •••••••• |
VOLUNTEER_USERNAME |
Web portal login | volunteer |
VOLUNTEER_PASSWORD |
Web portal password | •••••••• |
SESSION_SECRET |
64-char hex secret for cookies | 9609d564... |
CLOUDFLARE_API_TOKEN |
Wrangler deploy token | ALru7Y... |
CLOUDFLARE_ACCOUNT_ID |
Cloudflare account ID | e0518b2d... |
| Column | Purpose |
|---|---|
| A–K | Google Form responses (searched for email / membership number) |
| M | Attendance mark — written when attendee is found & checked in |
| O | Payment confirmation — written when payment is confirmed |
cd web
npm install
npx wrangler dev
# Opens http://localhost:8787The .dev.vars file (gitignored) provides secrets locally — see scripts/rebuild-devvars.ps1.
cd web
npx wrangler deployOr push to main — GitHub Actions (deploy-web.yml) deploys automatically.
First-time secret setup (run once):
# Set each secret in Cloudflare Workers:
echo "your-value" | npx wrangler secret put SECRET_NAMESee scripts/set-secrets.ps1 for the full automated setup script.
The worker is configured to run on auto-detect.ibtikar.tr. This requires:
ibtikar.trzone to be in the same Cloudflare account- Wrangler config
routesentry already set inwrangler.jsonc - DNS record created automatically by Cloudflare on first deploy
The Python scanner works on:
- Raspberry Pi 4 (Raspberry Pi OS)
- Windows / macOS / Linux laptops (any machine with a webcam)
cd pi
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -r requirements.txt
cp ../.env .env # Copy root .env
python scanner.py # Camera index 0
python scanner.py --camera 1 # External USB webcam
python scanner.py --no-window # Headless (no GUI)
python scanner.py --test # Decode example QR and exit (no camera needed)Full Pi setup: see pi/README.md
All endpoints except /api/health require a session cookie (set by /login).
| Method | Path | Description |
|---|---|---|
GET |
/ |
Redirect to /login or /app |
GET |
/login |
Login page |
POST |
/login |
Authenticate — sets ibtikar_session cookie |
GET |
/logout |
Clears session cookie |
GET |
/app |
Scanner app (protected) |
GET |
/api/health |
Config health check (public) |
GET |
/api/debug |
Live Sheet + VMS connectivity test |
POST |
/api/process |
Look up member (no writes) |
POST |
/api/attendance |
Mark attendance in Sheet and/or VMS |
POST |
/api/payment |
Confirm payment in Sheet and/or VMS |
{ "qrData": "2302002:member@example.com:b1ed6cdc-d9a9-4f08-ac48-54fe3eb52207" }Accepts three input formats:
- Full QR —
membershipNo:email:eventId - Email only —
member@example.com(usesETC_2026_EVENT_IDfrom env) - Membership # only —
2302002(usesETC_2026_EVENT_IDfrom env)
{
"success": true,
"results": {
"sheet": { "success": true, "row": 42 },
"vms": { "success": false, "error": "Registration not found in VMS" }
}
}Base URL: https://vms.ibtikar.tr/ms/membership-app/api
| Endpoint | Method | Description |
|---|---|---|
/login |
POST |
Get access token |
/event-registrations?eventId=X&membershipNumber=Y |
GET |
Find registration |
/event-registrations/{id}/approve?type=attendance |
POST |
Mark attendance |
/event-registrations/{id}/approve?type=payment |
POST |
Confirm payment |
- No secrets in code — all sensitive values via Cloudflare Worker secrets /
.dev.vars - Google Sheets JWT auth — implemented with Web Crypto API (no Node.js SDK, works in Workers edge runtime)
- Session auth — HMAC-SHA256 signed cookies, 8-hour TTL, constant-time comparison
- Encoding — all Arabic text in HTML uses
&#x...;entities to prevent encoding issues - Fallback logic — Sheet is primary; if member not found in Sheet, VMS is tried automatically
Workflow: .github/workflows/deploy-web.yml
Triggers on push to main when files in web/ change.
Required GitHub repository secrets:
| Secret | Value |
|---|---|
CLOUDFLARE_API_TOKEN |
Cloudflare API token with Workers edit permission |
CLOUDFLARE_ACCOUNT_ID |
Cloudflare account ID |