NFC Attendance Hub
A multi-tenant admin web app that acts as middleware between on-premise NFC student attendance apps and the Semaphore SMS API.
Features
- Multi-tenant Client Management — Register schools/orgs, each gets a unique API key
- Semaphore SMS Integration — Per-client Semaphore API key + sender name, credit balance display
- DB-based SMS Queue — Retry up to 3 attempts with exponential backoff, tracks failure reasons
- Failed SMS Dashboard — View failed messages with error details, manual retry button
- Reports — SMS stats per client, date filter, CSV export
- Admin Auth — Email/password login with NextAuth
Tech Stack
- Next.js 14 (App Router) + TypeScript
- Tailwind CSS + shadcn/ui-compatible components
- Prisma ORM (v5) + PostgreSQL
- NextAuth.js (credentials provider)
Quick Start (Docker)
cp .env.example .env
# Edit .env with your values
docker compose up -d
App available at: http://localhost:3000
Default admin: admin@nfchub.local / admin123
Development
npm install
cp .env.example .env
# Edit .env to point to a local PostgreSQL
npx prisma generate
npx prisma db push
npm run db:seed
npm run dev
SMS API Reference
On-premise NFC apps submit attendance events to:
POST /api/v1/sms/send
Header: X-API-Key: <client_api_key>
Content-Type: application/json
{
"student_name": "Juan Dela Cruz",
"student_id": "2024-001",
"parent_phone": "09171234567",
"event": "time_in", // "time_in" or "time_out"
"timestamp": "2024-01-15T08:30:00+08:00",
"message": "Optional custom message" // optional
}
Response
{ "success": true, "queueId": "...", "message": "SMS queued for delivery" }
SMS Queue Behavior
- Submission is accepted immediately (202 Accepted)
- Processing is triggered asynchronously
- Failed sends are retried up to 3 times with exponential backoff (2min, 4min, 8min)
- After 3 failures, SMS is marked FAILED and visible in the Failed SMS dashboard
Environment Variables
| Variable | Description | Required |
|---|---|---|
DATABASE_URL |
PostgreSQL connection string | Yes |
NEXTAUTH_URL |
Public app URL | Yes |
NEXTAUTH_SECRET |
Random secret for JWT | Yes |
INTERNAL_API_KEY |
Internal queue trigger key | Yes |
Docker Services
| Service | Port | Description |
|---|---|---|
app |
3000 | Next.js application |
postgres |
5433 (host) | PostgreSQL 16 |
Description
Languages
TypeScript
95.6%
Dockerfile
1.7%
CSS
1.1%
Shell
1.1%
JavaScript
0.5%