- Add shared ADIF parser utility (src/backend/utils/adif-parser.js) - parseADIF(): Parse ADIF format into QSO records - parseDCLResponse(): Parse DCL's JSON response format - normalizeBand() and normalizeMode(): Standardize band/mode names - Implement DCL service (src/backend/services/dcl.service.js) - fetchQSOsFromDCL(): Fetch from DCL API (ready for API availability) - parseDCLJSONResponse(): Parse example payload format - syncQSOs(): Update existing QSOs with DCL confirmations - Support DCL-specific fields: DCL_QSL_RCVD, DCL_QSLRDATE, DARC_DOK, MY_DARC_DOK - Refactor LoTW service to use shared ADIF parser - Remove duplicate parseADIF, normalizeBand, normalizeMode functions - Import from shared utility for consistency - Tested with example DCL payload - Successfully parses all 6 QSOs - Correctly extracts DCL confirmation data - Handles ADIF format with <EOR> delimiters Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
6.9 KiB
Default to using Bun instead of Node.js.
- Use
bun <file>instead ofnode <file>orts-node <file> - Use
bun testinstead ofjestorvitest - Use
bun build <file.html|file.ts|file.css>instead ofwebpackoresbuild - Use
bun installinstead ofnpm installoryarn installorpnpm install - Use
bun run <script>instead ofnpm run <script>oryarn run <script>orpnpm run <script> - Use
bunx <package> <command>instead ofnpx <package> <command> - Bun automatically loads .env, so don't use dotenv.
APIs
Bun.serve()supports WebSockets, HTTPS, and routes. Don't useexpress.bun:sqlitefor SQLite. Don't usebetter-sqlite3.Bun.redisfor Redis. Don't useioredis.Bun.sqlfor Postgres. Don't usepgorpostgres.js.WebSocketis built-in. Don't usews.- Prefer
Bun.fileovernode:fs's readFile/writeFile - Bun.$
lsinstead of execa.
Testing
Use bun test to run tests.
import { test, expect } from "bun:test";
test("hello world", () => {
expect(1).toBe(1);
});
Frontend
Use HTML imports with Bun.serve(). Don't use vite. HTML imports fully support React, CSS, Tailwind.
Server:
import index from "./index.html"
Bun.serve({
routes: {
"/": index,
"/api/users/:id": {
GET: (req) => {
return new Response(JSON.stringify({ id: req.params.id }));
},
},
},
// optional websocket support
websocket: {
open: (ws) => {
ws.send("Hello, world!");
},
message: (ws, message) => {
ws.send(message);
},
close: (ws) => {
// handle close
}
},
development: {
hmr: true,
console: true,
}
})
HTML files can import .tsx, .jsx or .js files directly and Bun's bundler will transpile & bundle automatically. <link> tags can point to stylesheets and Bun's CSS bundler will bundle.
<html>
<body>
<h1>Hello, world!</h1>
<script type="module" src="./frontend.tsx"></script>
</body>
</html>
With the following frontend.tsx:
import React from "react";
import { createRoot } from "react-dom/client";
// import .css files directly and it works
import './index.css';
const root = createRoot(document.body);
export default function Frontend() {
return <h1>Hello, world!</h1>;
}
root.render(<Frontend />);
Then, run index.ts
bun --hot ./index.ts
For more information, read the Bun API docs in node_modules/bun-types/docs/**.mdx.
Project: Quickawards by DJ7NT
Quickawards is a amateur radio award tracking application that calculates progress toward various awards based on QSO (contact) data.
Award System Architecture
The award system is JSON-driven and located in award-definitions/ directory. Each award has:
id: Unique identifier (e.g., "dld", "dxcc")name: Display namedescription: Short descriptioncaption: Detailed explanationcategory: Award category ("dxcc", "darc", etc.)rules: Award calculation logic
Award Rule Types
-
entity: Count unique entities (DXCC countries, states, grid squares)entityType: What to count ("dxcc", "state", "grid", "callsign")target: Number required for awardfilters: Optional filters (band, mode, etc.)displayField: Optional field to display
-
dok: Count unique DOK (DARC Ortsverband Kennung) combinationstarget: Number requiredconfirmationType: "dcl" (DARC Community Logbook)- Counts unique (DOK, band, mode) combinations
- Only DCL-confirmed QSOs count
-
points: Point-based awardsstations: Array of {callsign, points}target: Points requiredcountMode: "perStation", "perBandMode", or "perQso"
-
filtered: Filtered version of another awardbaseRule: The base entity rulefilters: Additional filters to apply
-
counter: Count QSOs or callsigns
Key Files
Backend Award Service: src/backend/services/awards.service.js
getAllAwards(): Returns all available award definitionscalculateAwardProgress(userId, award, options): Main calculation functioncalculateDOKAwardProgress(userId, award, options): DOK-specific calculationcalculatePointsAwardProgress(userId, award, options): Point-based calculationgetAwardEntityBreakdown(userId, awardId): Detailed entity breakdowngetAwardProgressDetails(userId, awardId): Progress with details
Database Schema: src/backend/db/schema/index.js
- QSO fields include:
darcDok,dclQslRstatus,dclQslRdate - DOK fields support DLD award tracking
- DCL confirmation fields separate from LoTW
Award Definitions: award-definitions/*.json
- Add new awards by creating JSON definition files
- Add filename to
loadAwardDefinitions()file list in awards.service.js
DLD Award Implementation (COMPLETED)
The DLD (Deutschland Diplom) award was recently implemented:
Definition: award-definitions/dld.json
{
"id": "dld",
"name": "DLD",
"description": "Deutschland Diplom - Confirm 100 unique DOKs on different bands/modes",
"caption": "Contact and confirm stations with 100 unique DOKs (DARC Ortsverband Kennung) on different band/mode combinations.",
"category": "darc",
"rules": {
"type": "dok",
"target": 100,
"confirmationType": "dcl",
"displayField": "darcDok"
}
}
Implementation Details:
- Function:
calculateDOKAwardProgress()insrc/backend/services/awards.service.js(lines 173-268) - Counts unique (DOK, band, mode) combinations
- Only DCL-confirmed QSOs count (
dclQslRstatus === 'Y') - Each unique DOK on each unique band/mode counts separately
- Returns worked, confirmed counts and entity breakdowns
Database Fields Used:
darcDok: DOK identifier (e.g., "F03", "P30", "G20")band: Band (e.g., "80m", "40m", "20m")mode: Mode (e.g., "CW", "SSB", "FT8")dclQslRstatus: DCL confirmation status ('Y' = confirmed)dclQslRdate: DCL confirmation date
Documentation: See docs/DOCUMENTATION.md for complete documentation including DLD award example.
Adding New Awards
To add a new award:
- Create JSON definition in
award-definitions/ - Add filename to
loadAwardDefinitions()insrc/backend/services/awards.service.js - If new rule type needed, add calculation function
- Add type handling in
calculateAwardProgress()switch statement - Add type handling in
getAwardEntityBreakdown()if needed - Update documentation in
docs/DOCUMENTATION.md - Test with sample QSO data
Confirmation Systems
-
LoTW (Logbook of The World): ARRL's confirmation system
- Fields:
lotwQslRstatus,lotwQslRdate - Used for DXCC, WAS, VUCC, most awards
- Fields:
-
DCL (DARC Community Logbook): DARC's confirmation system
- Fields:
dclQslRstatus,dclQslRdate - Required for DLD award
- German amateur radio specific
- Fields:
Recent Commits
c982dcd: feat: implement DLD (Deutschland Diplom) award322ccaf: docs: add DLD (Deutschland Diplom) award documentation