Joerg 512f4f682e Add comprehensive README documentation
- Describe project purpose and features
- Document tech stack (Bun, Elysia, SvelteKit, SQLite, Drizzle)
- Add project structure overview
- Include setup and installation instructions
- Document all API endpoints
- Include database schema for all tables
- Explain background job queue system
- Document LoTW sync logic and pagination features
- Add development commands

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-15 22:11:15 +01:00

Ham Radio Award Portal

A web application for amateur radio operators to track QSOs (contacts) and award progress using Logbook of the World (LoTW) data.

Features

  • User Authentication: Register and login with callsign, email, and password
  • LoTW Integration: Sync QSOs from ARRL's Logbook of the World
    • Background job queue for non-blocking sync operations
    • Incremental sync using last confirmation date
    • Wavelog-compatible download logic with proper validation
    • One sync job per user enforcement
  • QSO Log: View and manage confirmed QSOs
    • Pagination support for large QSO collections
    • Filter by band, mode, and confirmation status
    • Statistics dashboard (total QSOs, confirmed, DXCC entities, bands)
    • Delete all QSOs with confirmation
  • Settings: Configure LoTW credentials securely

Tech Stack

Backend

  • Runtime: Bun
  • Framework: Elysia.js
  • Database: SQLite with Drizzle ORM
  • Authentication: JWT tokens

Frontend

  • Framework: SvelteKit
  • Language: JavaScript
  • Styling: Custom CSS

Project Structure

award/
├── src/
│   ├── backend/
│   │   ├── config/
│   │   │   ├── database.js     # Database connection
│   │   │   └── jwt.js          # JWT configuration
│   │   ├── db/
│   │   │   └── schema/
│   │   │       └── index.js    # Database schema (users, qsos, sync_jobs)
│   │   ├── services/
│   │   │   ├── auth.service.js        # User authentication
│   │   │   ├── lotw.service.js        # LoTW sync & QSO management
│   │   │   └── job-queue.service.js   # Background job queue
│   │   └── index.js            # API routes and server
│   └── frontend/
│       ├── src/
│       │   ├── lib/
│       │   │   ├── api.js      # API client
│       │   │   └── stores.js   # Svelte stores (auth)
│       │   └── routes/
│       │       ├── +page.svelte         # Main menu
│       │       ├── login/+page.svelte   # Login page
│       │       ├── register/+page.svelte # Registration page
│       │       ├── qsos/+page.svelte    # QSO log with pagination
│       │       └── settings/+page.svelte # Settings & LoTW credentials
│       └── package.json
├── award.db                   # SQLite database (auto-created)
├── drizzle.config.js          # Drizzle ORM configuration
├── package.json
└── README.md

Setup

Prerequisites

  • Bun v1.3.6 or later

Installation

  1. Clone the repository:
git clone <repository-url>
cd award
  1. Install dependencies:
bun install
  1. Set up environment variables (optional): Create a .env file in the project root:
JWT_SECRET=your-secret-key-here

If not provided, a default secret will be used.

  1. Initialize the database:
bun run db:push

This creates the SQLite database with required tables (users, qsos, sync_jobs).

Running the Application

Start both backend and frontend:

# Start backend (port 3001)
bun run backend/index.js

# Start frontend (port 5173)
cd src/frontend && bun run dev

Or use the convenience scripts:

# Backend only
bun run backend

# Frontend only
bun run frontend

The application will be available at:

API Endpoints

Authentication

  • POST /api/auth/register - Register new user
  • POST /api/auth/login - Login user
  • GET /api/auth/me - Get current user profile
  • PUT /api/auth/lotw-credentials - Update LoTW credentials

LoTW Sync

  • POST /api/lotw/sync - Queue a LoTW sync job (returns job ID)

Jobs

  • GET /api/jobs/:jobId - Get job status
  • GET /api/jobs/active - Get user's active job
  • GET /api/jobs - Get recent jobs (query: ?limit=10)

QSOs

  • GET /api/qsos - Get user's QSOs with pagination
    • Query parameters: ?page=1&limit=100&band=20m&mode=CW&confirmed=true
  • GET /api/qsos/stats - Get QSO statistics
  • DELETE /api/qsos/all - Delete all QSOs (requires confirmation)

Health

  • GET /api/health - Health check endpoint

Database Schema

Users Table

CREATE TABLE users (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  email TEXT UNIQUE NOT NULL,
  password TEXT NOT NULL,
  callsign TEXT NOT NULL,
  lotwUsername TEXT,
  lotwPassword TEXT,
  createdAt TEXT NOT NULL
);

QSOs Table

CREATE TABLE qsos (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  userId INTEGER NOT NULL,
  callsign TEXT NOT NULL,
  qsoDate TEXT NOT NULL,
  timeOn TEXT NOT NULL,
  band TEXT,
  mode TEXT,
  entity TEXT,
  grid TEXT,
  lotwQslRstatus TEXT,
  lotwQslRdate TEXT,
  FOREIGN KEY (userId) REFERENCES users(id)
);

Sync Jobs Table

CREATE TABLE sync_jobs (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  userId INTEGER NOT NULL,
  status TEXT NOT NULL, -- pending, running, completed, failed
  type TEXT NOT NULL,   -- lotw_sync
  startedAt INTEGER,
  completedAt INTEGER,
  result TEXT,          -- JSON
  error TEXT,
  createdAt INTEGER NOT NULL,
  FOREIGN KEY (userId) REFERENCES users(id)
);

Features in Detail

Background Job Queue

The application uses an in-memory job queue system for async operations:

  • Jobs are persisted to database for recovery
  • Only one active job per user (enforced at queue level)
  • Status tracking: pending → running → completed/failed
  • Real-time progress updates via job result field
  • Client polls job status every 2 seconds

LoTW Sync Logic

Following Wavelog's proven approach:

  1. First sync: Uses date 2000-01-01 to retrieve all QSOs
  2. Subsequent syncs: Uses MAX(lotwQslRdate) from database
  3. Validation:
    • Checks for "Username/password incorrect" in response
    • Validates file starts with "ARRL Logbook of the World Status Report"
  4. Timeout handling: 30-second connection timeout
  5. Query parameters: Matches Wavelog's LoTW download

Pagination

  • Default page size: 100 QSOs per page
  • Supports custom page size via limit parameter
  • Shows page numbers with ellipsis for large page counts
  • Displays "Showing X-Y of Z" info
  • Previous/Next navigation buttons

Development

Database Migrations

# Push schema changes to database
bun run db:push

# Open Drizzle Studio (database GUI)
bun run db:studio

Linting

bun run lint

License

MIT

Credits

Description
No description provided
Readme 1.9 MiB
Languages
Svelte 53.2%
JavaScript 45.4%
CSS 0.8%
HTML 0.5%