From 051f95f00d8265b5851768b5c79f7b1a1b07bd01 Mon Sep 17 00:00:00 2001 From: SowinskiBraeden Date: Wed, 8 Jul 2026 10:26:45 -0700 Subject: [PATCH] update project entries --- public/content/index.json | 140 ++++++++----------- public/content/projects/myboker-org.md | 1 + public/content/projects/reforger-mods-api.md | 45 ++++++ public/scripts/content.js | 8 +- 4 files changed, 105 insertions(+), 89 deletions(-) create mode 100644 public/content/projects/reforger-mods-api.md diff --git a/public/content/index.json b/public/content/index.json index 9acc4ab..e2b99aa 100644 --- a/public/content/index.json +++ b/public/content/index.json @@ -1,24 +1,26 @@ { "projects": [ { - "title": "Schedule Generator App", - "slug": "schedule-generator-app", + "title": "Reforger Mods API", + "slug": "reforger-mods-api", "type": "projects", - "summary": "A desktop application to interact with my schedule generator algorithm. The algorithm is written in Python and generates master and student timetables.", + "summary": "A public, unofficial REST API for Arma Reforger Workshop metadata, built with caching, background refresh jobs, rate limiting, and production reliability in mind.", "label": "Project", - "date": "2023-08-01", - "status": "Completed", - "github": "https://github.com/SowinskiBraeden/ScheduleGeneratorApp", - "demo": null, + "date": "2026-07-06", + "status": "Active", + "github": "https://github.com/SowinskiBraeden/ReforgerWorkshopAPI", + "demo": "https://api.reforgermods.net", "featured": true, "order": 1, "tags": [ - "Python", - "Desktop App", - "Scheduling", - "Data Processing" + "Go", + "REST API", + "Caching", + "Web Scraping", + "Observability", + "Documentation" ], - "path": "./public/content/projects/schedule-generator-app.md" + "path": "./public/content/projects/reforger-mods-api.md" }, { "title": "myboker.org", @@ -28,7 +30,7 @@ "label": "Project", "date": "2026-06-28", "status": "Active", - "github": null, + "github": "https://github.com/SowinskiBraeden/boker", "demo": "https://myboker.org", "featured": true, "order": 2, @@ -42,6 +44,21 @@ ], "path": "./public/content/projects/myboker-org.md" }, + { + "title": "Schedule Generator App", + "slug": "schedule-generator-app", + "type": "projects", + "summary": "A desktop application to interact with my schedule generator algorithm. The algorithm is written in Python and generates master and student timetables.", + "label": "Project", + "date": "2023-08-01", + "status": "Completed", + "github": "https://github.com/SowinskiBraeden/ScheduleGeneratorApp", + "demo": null, + "featured": true, + "order": 3, + "tags": ["Python", "Desktop App", "Scheduling", "Data Processing"], + "path": "./public/content/projects/schedule-generator-app.md" + }, { "title": "DayZ Reforger Bot", "slug": "dayz-reforger-bot", @@ -53,35 +70,9 @@ "github": "https://github.com/SowinskiBraeden/dayz-reforger", "demo": null, "featured": true, - "order": 3, - "tags": [ - "Node.js", - "Discord.js", - "MongoDB", - "Analytics", - "Nitrado" - ], - "path": "./public/content/projects/dayz-reforger-bot.md" - }, - { - "title": "Vairo Performance Website", - "slug": "vairo-performance-website", - "type": "projects", - "summary": "A freelance website project for Vairo Performance, including site design, content structure, domain setup, and launch support for a professional online presence.", - "label": "Project", - "date": "2026-03-01", - "status": "Client Work", - "github": null, - "demo": "https://vairo-performance.com", - "featured": true, "order": 4, - "tags": [ - "Freelance", - "Web Design", - "Domain Setup", - "Client Work" - ], - "path": "./public/content/projects/vairo-performance-website.md" + "tags": ["Node.js", "Discord.js", "MongoDB", "Analytics", "Nitrado"], + "path": "./public/content/projects/dayz-reforger-bot.md" }, { "title": "Lines Police CAD", @@ -95,12 +86,7 @@ "demo": "https://www.linespolice-cad.com/", "featured": false, "order": 5, - "tags": [ - "Node.js", - "Express", - "MongoDB", - "Web App" - ], + "tags": ["Node.js", "Express", "MongoDB", "Web App"], "path": "./public/content/projects/lines-police-cad.md" }, { @@ -115,14 +101,24 @@ "demo": null, "featured": false, "order": 6, - "tags": [ - "HTML", - "CSS", - "JavaScript", - "Portfolio" - ], + "tags": ["HTML", "CSS", "JavaScript", "Portfolio"], "path": "./public/content/projects/portfolio.md" }, + { + "title": "Vairo Performance Website", + "slug": "vairo-performance-website", + "type": "projects", + "summary": "A freelance website project for Vairo Performance, including site design, content structure, domain setup, and launch support for a professional online presence.", + "label": "Project", + "date": "2026-03-01", + "status": "Client Work", + "github": null, + "demo": "https://vairo-performance.com", + "featured": false, + "order": 7, + "tags": ["Freelance", "Web Design", "Domain Setup", "Client Work"], + "path": "./public/content/projects/vairo-performance-website.md" + }, { "title": "School Management API", "slug": "school-management-api", @@ -134,13 +130,8 @@ "github": "https://github.com/SowinskiBraeden/school-management-api", "demo": null, "featured": false, - "order": 7, - "tags": [ - "Go", - "API", - "Documentation", - "Education Tech" - ], + "order": 8, + "tags": ["Go", "API", "Documentation", "Education Tech"], "path": "./public/content/projects/school-management-api.md" }, { @@ -154,12 +145,8 @@ "github": "https://github.com/SowinskiBraeden/XP-System", "demo": null, "featured": false, - "order": 8, - "tags": [ - "Web App", - "Education Tech", - "Gamification" - ], + "order": 9, + "tags": ["Web App", "Education Tech", "Gamification"], "path": "./public/content/projects/xp-system.md" } ], @@ -176,13 +163,7 @@ "demo": null, "featured": true, "order": 2, - "tags": [ - "Hackathon", - "RF", - "Machine Learning", - "Geolocation", - "BCIT" - ], + "tags": ["Hackathon", "RF", "Machine Learning", "Geolocation", "BCIT"], "path": "./public/content/updates/red-team-hackathon-2026.md" }, { @@ -197,11 +178,7 @@ "demo": null, "featured": true, "order": 3, - "tags": [ - "Computing Club", - "Executive Member", - "BCIT" - ], + "tags": ["Computing Club", "Executive Member", "BCIT"], "path": "./public/content/updates/joined-bcit-computing-club.md" }, { @@ -216,12 +193,7 @@ "demo": null, "featured": true, "order": 1, - "tags": [ - "Leadership", - "BCIT", - "Community", - "C4A" - ], + "tags": ["Leadership", "BCIT", "Community", "C4A"], "path": "./public/content/updates/appointed-president-c4a.md" } ] diff --git a/public/content/projects/myboker-org.md b/public/content/projects/myboker-org.md index 6552c03..873ca48 100644 --- a/public/content/projects/myboker-org.md +++ b/public/content/projects/myboker-org.md @@ -7,6 +7,7 @@ date: 2026-06-28 status: Active featured: true order: 2 +github: https://github.com/SowinskiBraeden/boker live: https://myboker.org tags: Python, Flask, Jinja, Chart.js, PostgreSQL, Analytics --- diff --git a/public/content/projects/reforger-mods-api.md b/public/content/projects/reforger-mods-api.md new file mode 100644 index 0000000..2232d2b --- /dev/null +++ b/public/content/projects/reforger-mods-api.md @@ -0,0 +1,45 @@ +--- +title: Reforger Mods API +slug: reforger-mods-api +summary: A public, unofficial REST API for Arma Reforger Workshop metadata, built with caching, background refresh jobs, rate limiting, and production reliability in mind. +label: Project +date: 2026-07-06 +status: Active +featured: true +order: 1 +github: https://github.com/SowinskiBraeden/ReforgerWorkshopAPI +live: https://api.reforgermods.net +tags: Go, REST API, Web Scraping, Observability, Documentation +--- + +## Overview + +The Arma Reforger Workshop is primarily a website, not a developer API. Panels, hosting tools, bots, and dashboards that need structured mod data have no clean way to get it. I built Reforger Mods API to fill that gap. + +It is a public REST API, live at [api.reforgermods.net](https://api.reforgermods.net), that fetches and normalizes Workshop metadata into predictable, documented responses for developers. After the most recent relaunch, it started receiving identified traffic from third-party, which makes it one of the first projects I have built that is genuinely used beyond my own stack. + +## What It Does + +- Search and list Workshop mods with structured, filterable responses +- Retrieve detailed mod metadata normalized from raw Workshop pages +- Cache responses to avoid hammering upstream on repeated lookups +- Serve stale data while background refresh jobs revalidate it +- Return `202 Accepted` on cold cache misses so consumers are not blocked waiting on a full upstream scrape + +## Reliability and API Design + +A scraper-backed API has an obvious weak point: if the upstream source is slow or flaky, every request suffers. I treated reliability as a first-class concern from the start. + +Cached responses return fast. When data is stale, it gets served immediately while a refresh job runs in the background. On a cold miss, the API returns `202 Accepted` with a job reference instead of making the caller wait. Rate limiting and defensive scraper error handling mean that one bad upstream page does not cascade into a broken API response. + +Internal metrics track traffic, cache hit rates, refresh job behavior, and errors, so I can actually tell what the service is doing in production rather than guessing. + +## Documentation + +The API includes public-facing documentation covering all endpoints, response shapes, cache behavior, and how to handle `202 Accepted` responses correctly. Getting the docs right felt important, if external tools are depending on this, they need to be able to understand it without digging through source code. + +Live API and docs: [api.reforgermods.net](https://api.reforgermods.net) + +## Notes + +This project sits at the intersection of a few things I am consistently interested in: backend infrastructure, reliability engineering, and practical tooling for game communities. It is also the first project where I have had to think seriously about backwards compatibility and defensive API design — once external consumers exist, breaking changes have real consequences. That constraint pushed me to build it more carefully than I would have if it were purely internal. diff --git a/public/scripts/content.js b/public/scripts/content.js index 3d0ca0b..2ae3283 100644 --- a/public/scripts/content.js +++ b/public/scripts/content.js @@ -137,12 +137,10 @@ links.push(`Read More`); - if (entry.github) { - links.push(`GitHub`); - } - if (entry.demo) { links.push(`Live`); + } else if (entry.github) { + links.push(`GitHub`); } return `
${links.join("")}
`; @@ -316,8 +314,8 @@

${escapeHtml(entry.summary || "")}

${renderTags(entry.tags || [], "entry-tag-list")}
- ${entry.github ? `GitHub` : ""} ${entry.demo ? `Live` : ""} + ${entry.github ? `GitHub` : ""}