Skip to content

synqronlabs/gla-qrify-api

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

11 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Garba Night QRify Backend API

A Go-based backend API for managing attendee access to Garba Night by GLA, including venue entry, refreshments, and dinner using QR code authentication.

Table of Contents

Overview

This API manages event attendee access through a QR code system. Attendees receive JWT tokens encoded in QR codes that grant access to different parts of the event (dance, refreshments, dinner) based on their permissions.

Attendee Workflows

The system supports different types of attendees with different access patterns:

  1. Dance-only attendees (has_dance_access: true, has_dinner_access: false):

    • Use /venue/dance to enter the venue
    • Can claim refreshments via /refreshment
  2. Dinner-only attendees (has_dance_access: false, has_dinner_access: true):

    • Use /dinner directly (automatically records venue entry)
  3. Full access attendees (has_dance_access: true, has_dinner_access: true):

    • Can enter via /venue/dance early for dancing and refreshments
    • Later use /dinner to claim dinner (venue entry already recorded)
    • Alternatively, can go directly to /dinner if arriving only for dinner (automatically records venue entry)

Authentication

JWT Authentication (Protected Routes)

Protected routes require a JWT token in the Authorization header:

Authorization: Bearer <jwt_token>

The JWT token contains:

  • attendee_id: Unique identifier for the attendee
  • has_dance_access: Boolean indicating dance access permission
  • has_dinner_access: Boolean indicating dinner access permission

Admin Authentication

Admin routes require an admin secret in the X-Admin-Secret header:

X-Admin-Secret: <admin_secret>

API Endpoints

Health Check

GET /ping

Check if the API is running.

Authentication: None required

Response:

{
  "message": "pong"
}

cURL Example:

curl -X GET http://localhost:8080/ping

Protected Entry Routes

All entry routes require JWT authentication and are prefixed with /api.

POST /api/venue/dance

Mark venue entry for attendees with dance access.

Authentication: JWT token required
Permissions: has_dance_access must be true

Response (Success):

{
  "message": "Venue entry marked successfully",
  "attendee_id": "A1B2C3D4E",
  "attendee_name": "John Doe",
  "venue_entry_at": "2025-09-12T15:30:00Z"
}

Response (Error - No Dance Access):

{
  "error": "Access denied: No dance access"
}

Response (Error - Already Entered):

{
  "error": "Already entered venue or attendee not found"
}

cURL Example:

curl -X POST http://localhost:8080/api/venue/dance \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -H "Content-Type: application/json"

POST /api/refreshment

Mark refreshment entry for attendees with dance access.

Authentication: JWT token required
Permissions: has_dance_access must be true

Response (Success):

{
  "message": "Refreshment entry marked successfully",
  "attendee_id": "A1B2C3D4E",
  "attendee_name": "John Doe",
  "refreshment_claimed_at": "2025-09-12T16:45:00Z"
}

Response (Error - Not Eligible):

{
  "error": "Not eligible for refreshments or already claimed"
}

cURL Example:

curl -X POST http://localhost:8080/api/refreshment \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -H "Content-Type: application/json"

POST /api/dinner

Mark dinner entry for attendees with dinner access. This endpoint also automatically records venue entry if the attendee hasn't entered the venue yet.

Authentication: JWT token required
Permissions: has_dinner_access must be true

Response (Success):

{
  "message": "Dinner entry marked successfully",
  "attendee_id": "F5G6H7I8J",
  "attendee_name": "Jane Smith",
  "dinner_claimed_at": "2025-09-12T20:30:00Z",
  "venue_entry_at": "2025-09-12T20:30:00Z"
}

Response (Error - Not Eligible):

{
  "error": "Not eligible for dinner or already claimed"
}

cURL Example:

curl -X POST http://localhost:8080/api/dinner \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -H "Content-Type: application/json"

Admin Routes

All admin routes require admin secret authentication and are prefixed with /admin.

GET /admin/qr/:attendee_id

Generate a QR code containing JWT token for a specific attendee.

Authentication: Admin secret required

Parameters:

  • attendee_id (path): The unique identifier of the attendee

Response: PNG image (QR code)

Response Headers:

Content-Type: image/png
Content-Disposition: inline; filename=qr-{attendee_id}.png

Response (Error - Attendee Not Found):

{
  "error": "Attendee not found"
}

cURL Example:

# Save QR code to file
curl -X GET http://localhost:8080/admin/qr/A1B2C3D4E \
  -H "X-Admin-Secret: your_admin_secret_here" \
  -o qr-A1B2C3D4E.png

# View QR code info without saving
curl -X GET http://localhost:8080/admin/qr/A1B2C3D4E \
  -H "X-Admin-Secret: your_admin_secret_here" \
  -I

GET /admin/token/:attendee_id

Generate a JWT token for a specific attendee and return it as JSON.

Authentication: Admin secret required

Parameters:

  • attendee_id (path): The unique identifier of the attendee

Response (Success):

{
  "attendee_id": "A1B2C3D4E",
  "attendee_name": "John Doe",
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "expires_in": "24h"
}

Response (Error - Attendee Not Found):

{
  "error": "Attendee not found"
}

cURL Example:

curl -X GET http://localhost:8080/admin/token/A1B2C3D4E \
  -H "X-Admin-Secret: your_admin_secret_here" \
  -H "Content-Type: application/json"

POST /admin/attendees

Create a new attendee in the database.

Authentication: Admin secret required

Request Body:

{
  "id": "A1B2C3D4E",
  "full_name": "John Doe",
  "phone_number": "+1234567890",
  "has_dance_access": true,
  "has_dinner_access": false
}

Required Fields:

  • id: Unique identifier for the attendee
  • full_name: Full name of the attendee
  • phone_number: Contact phone number

Optional Fields:

  • has_dance_access: Boolean (default: false)
  • has_dinner_access: Boolean (default: false)

Response (Success):

{
  "message": "Attendee created successfully",
  "attendee": {
    "id": "A1B2C3D4E",
    "full_name": "John Doe",
    "phone_number": "+1234567890",
    "has_dance_access": true,
    "has_dinner_access": false,
    "created_at": "2025-09-12T14:30:00Z",
    "updated_at": "2025-09-12T14:30:00Z"
  }
}

Response (Error - Duplicate ID):

{
  "error": "Attendee with this ID already exists"
}

Response (Error - Missing Required Field):

{
  "error": "Full name is required"
}

cURL Example:

curl -X POST http://localhost:8080/admin/attendees \
  -H "X-Admin-Secret: your_admin_secret_here" \
  -H "Content-Type: application/json" \
  -d '{
    "id": "A1B2C3D4E",
    "full_name": "John Doe",
    "phone_number": "+1234567890",
    "has_dance_access": true,
    "has_dinner_access": false
  }'

Error Responses

Common HTTP Status Codes

  • 200 OK: Request successful
  • 201 Created: Resource created successfully
  • 400 Bad Request: Invalid request format or missing required fields
  • 401 Unauthorized: Missing or invalid authentication
  • 403 Forbidden: Access denied (insufficient permissions)
  • 404 Not Found: Resource not found
  • 409 Conflict: Resource already exists or action not allowed
  • 500 Internal Server Error: Server error

Error Response Format

{
  "error": "Error message describing what went wrong"
}

Some errors may include additional details:

{
  "error": "Invalid JSON format",
  "details": "json: cannot unmarshal string into Go struct field..."
}

Environment Variables

Required environment variables:

  • DATABASE_URL: PostgreSQL connection string
  • JWT_SECRET: Secret key for JWT token signing/verification
  • ADMIN_SECRET: Secret key for admin route authentication
  • PORT (optional): Server port (default: 8080)

Example .env file:

DATABASE_URL=postgres://username:password@localhost:5432/garba_qrify
JWT_SECRET=your-super-secret-jwt-key-here
ADMIN_SECRET=your-admin-secret-here
PORT=8080

Usage Examples

Complete Workflow Example

  1. Create an attendee (Admin):
curl -X POST http://localhost:8080/admin/attendees \
  -H "X-Admin-Secret: your_admin_secret_here" \
  -H "Content-Type: application/json" \
  -d '{
    "id": "A1B2C3D4E",
    "full_name": "John Doe",
    "phone_number": "+1234567890",
    "has_dance_access": true,
    "has_dinner_access": true
  }'
  1. Generate QR code for attendee (Admin):
curl -X GET http://localhost:8080/admin/qr/A1B2C3D4E \
  -H "X-Admin-Secret: your_admin_secret_here" \
  -o john-doe-qr.png
  1. Get JWT token for testing (Admin):
curl -X GET http://localhost:8080/admin/token/A1B2C3D4E \
  -H "X-Admin-Secret: your_admin_secret_here"
  1. Attendee enters venue for dance:
curl -X POST http://localhost:8080/api/venue/dance \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
  1. Attendee claims refreshments:
curl -X POST http://localhost:8080/api/refreshment \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
  1. Attendee claims dinner (automatically records venue entry if not already done):
curl -X POST http://localhost:8080/api/dinner \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Notes

  • JWT tokens have different expiration times:
    • QR code tokens: 30 days
    • API tokens: 24 hours
  • Attendees can only perform actions they have access to (dance/dinner)
  • Each action (venue entry, refreshments, dinner) can only be performed once per attendee
  • Venue entry for dance is handled separately via /venue/dance endpoint
  • Dinner entry automatically handles venue entry - no separate venue entry endpoint needed for dinner attendees
  • All timestamps are returned in ISO 8601 format (UTC)

About

Go-based API for QR code authentication at Garba Night

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages