Releases: PGAN-Dev/PoracleWeb.NET
v2.8.0 — Compose simplification, favicon, invasion fixes
Changed
-
Docker compose is now shipped as
docker-compose.yml.example(#228): following the.env/.env.examplepattern,docker-compose.ymlis now gitignored and users copy the example on first install. Upstream tweaks to the example no longer clobber local customizations. The compose file also switched toenv_file: .envinstead of enumerating every variable underenvironment:, shrinking it by ~35 lines and making.envthe single source of configuration truth. The default image is nowghcr.io/pgan-dev/poracleweb.net:latest(pull) to match the README Quick Start; the local-build andbuild:alternatives remain as commented options.Upgrade (existing self-hosted users):
cp docker-compose.yml docker-compose.yml.bak # back up any local edits first git pull cp docker-compose.yml.example docker-compose.yml # re-apply any customizations from docker-compose.yml.bak (prefer docker-compose.override.yml for future edits) docker compose up -d --force-recreate
No
.envchanges are required — existing.envfiles remain compatible.Two things to know after upgrade:
- Existing users will be logged out once. JWT issuer/audience defaults changed from
Pgan.PoracleWebNet/Pgan.PoracleWebNet.ApptoPoracleWeb/PoracleWeb.App. Previously issued tokens fail validation and users re-login via Discord/Telegram — no data loss, just a single sign-in prompt. To keep the old values, setJWT_ISSUER=Pgan.PoracleWebNetandJWT_AUDIENCE=Pgan.PoracleWebNet.Appin.envbefore restarting. - Default image source changed. The example compose now pulls
ghcr.io/pgan-dev/poracleweb.net:latest. If you previously relied on a locally builtporacleweb.net:latest, uncomment the local-build alternative in yourdocker-compose.ymlbefore runningdocker compose up -dor you'll pull the published image instead.
- Existing users will be logged out once. JWT issuer/audience defaults changed from
Added
- Admin-configurable favicon (#218): new
favicon_urlsite setting under the Branding group in Admin → Settings. Admins can point to any hosted image (.ico,.png,.svg; 32×32 square recommended); leaving it empty falls back to the bundled default. Includes a live 32×32 preview next to the input and an inline warning that browsers aggressively cache favicons — users must clear their browser cache or hard-refresh (Ctrl+F5 / Cmd+Shift+R) to see the new icon. Applied at runtime by mutating the<link rel="icon">href via an Angular effect, mirroring the existing custom-title pattern. Exposed on the public settings endpoint so it loads pre-auth. - Optional
JWT_ISSUER/JWT_AUDIENCEenv vars (#228): documented in.env.examplewith sensible defaults (PoracleWebandPoracleWeb.App) applied byProgram.cswhen unset, so existing users don't need to add them. Override only if you need distinct token identities across deployments. - Giovanni quick pick (#221): split out while fixing the Rocket Leaders pick. New
invasion-giovannidefault quick pick tracks Giovanni encounters (gruntType=giovanni). Kept separate from "Rocket Leaders" because Giovanni spawns from the Super Rocket Radar only, while Sierra/Cliff/Arlo come from standard Rocket Radars — users typically want them on distinct alert profiles.
Fixed
- Invasion grunt icon now reflects the rule's gender filter (#224): for the 18 typed grunts (bug, fire, water, etc.), rules filtered by Male or Female now render the actual gendered grunt artwork from PogoAssets (
uicons/invasion/<character_id>.png) by mapping(grunt_type, gender)to the NianticInvasionCharacterenum — e.g. Water + Female → invasion 38, Water + Male → 39. "Any" gender keeps the existing Pokémon-type badge. The edit dialog preview updates live via atoSignalon the gender form control. - Editing an invasion alarm's gender created a duplicate row (#224): PoracleNG dedups invasion tracking by the natural key
(grunt_type, gender), so PUT-ing the row with a new gender caused a fresh insert instead of updating the referenced uid — leaving the original row as a stale duplicate.InvasionService.UpdateAsyncnow detects the insert response and deletes the old uid, tolerating network/timeout failures on the cleanup delete with a Warning log. - i18n: invasion grunt type labels (#223): hardcoded English grunt type names in
invasion.constants.ts(DISPLAY_NAMES) andinvasion-add-dialog.component.ts(GRUNT_TYPES[].name) bypassed the i18n system, so users on non-English locales still saw "Mixed Grunt", "Shadow", "Cliff", "Fire", etc. in English. AddedINVASIONS.GRUNT_TYPES.*(26 keys),INVASIONS.EVENT_TYPES.*(3 keys), andINVASIONS.GENDER_SUFFIX_MALE/FEMALEto all 11 locale files. ReplacedDISPLAY_NAMESwithGRUNT_DISPLAY_KEYS+getGruntDisplayKey()and added agetGruntDisplayName(gruntType, gender, translate)composer that appends a translated(Male)/(Female)suffix for the gender-fixed grunts (mixed/decoy). Updated add/edit dialog, invasion list, and profile-overview consumers. - Mobile: Pokemon list search/filter bar no longer leaves a 56px gap above itself when sticky.
topchanged from56pxto0sincemat-sidenav-contentis the scroll container and already sits below the app toolbar. Tightened the mobile (max-width: 599px) layout so the search field, quick-filter pills, and meta row stack cleanly at 390px viewports. (#213) - Mixed grunt gender variants (#221): the Mixed entry in the add-invasion dialog was a single checkbox with one icon, hiding the fact that Mixed grunts have male/female variants with distinct invasion character IDs (Male 4 — starter line, Female 5 — Snorlax line). The dialog now offers separate Male/Female checkboxes, and
getGruntIconUrl/getDisplayNameuse the alarm'sgenderto render the correct icon and a "(Male)"/"(Female)" suffix in the list and edit views. Decoy grunts default to invasion ID 46 (female) since the male variant (45) exists in the masterfile but does not spawn in-game. - "Rocket Leaders" quick pick subscribed users to the wrong grunts (#221): the
invasion-leaderpick sentgruntType=mixed, which in PoracleNG is the untypedCHARACTER_GRUNT_MALE/FEMALEgrunt, not Sierra/Cliff/Arlo. Applying the pick now fans out to three invasion alarms with the real leader grunt types (cliff,arlo,sierra) via a single bulk-create. Users who previously applied the broken pick should unapply and re-apply it to replace their stalemixedalarms. AddedQuickPickDefaultsTeststo assert default invasion picks use valid PoracleNGgrunt_typevalues and to regression-guard againstmixedbeing used for leaders. - Invasion grunt icons and labels (#216):
mixedgrunts rendered as "Rocket Leader" with the Cliff icon (invasion ID 41 =EXECUTIVE_CLIFF), anddecoygrunts rendered with the Electric icon (invasion ID 50 =ELECTRIC_GRUNT_MALE). Corrected both to their real NianticInvasionCharacterIDs —mixed→ 4 (untyped grunt),decoy→ 46 (female decoy; the male variant 45 exists in the masterfile but does not spawn in-game). Added missing grunt types surfaced by PoracleNG:darkness(Shadow) and the Rocket Leaderscliff/arlo/sierra. Also replaced the empty-string fallback ingetGruntIconUrl()with a generic unknown-grunt icon so an unmappedgrunt_typerenders a valid placeholder instead of a broken image. Updated bothinvasion.constants.tsand the add-invasion dialog. See the "Mixed grunt gender variants" entry above for the related add-dialog UX work.
v2.7.0 — i18n completion, release automation, dependabot
Highlights
Added
- Three-channel Docker release process (
:latest,:beta,:pr-<n>) — public testers can opt into pre-release builds before an official release is cut. Nightly prune workflow keeps the registry tidy. SeeTESTING.md. - Automated PR labeling & release-notes generation —
pr-labeler.ymlapplies Conventional-Commit labels from branch prefix or PR title;.github/release.ymlgroups labeled PRs for GitHub's auto-generated notes. - Dependabot — weekly grouped updates for NuGet, npm, GitHub Actions, and Docker base images. Angular/Material majors pinned for manual coordination.
- Dependabot auto-merge —
.github/workflows/dependabot-auto-merge.ymlauto-approves and queues patch bumps, grouped bundles, and GH Actions minor bumps (gated on CI).
Fixed
- i18n raw-token rendering:
QUESTS.CONFIRM_DELETE_SELECTED/INVASIONS.CONFIRM_DELETE_SELECTEDno longer render as literal keys on the bulk-delete dialog. (#209) - i18n 277-key coverage gap across 10 non-English locales — every missing key now has a native translation (2,770 translations total: fr, de, es, nl, it, pt, pt-BR, pl, da, sv). Native-speaker review welcome in discussion #211.
- Documentation branding: 88 remaining bare
PoracleWebreferences replaced withPoracleWeb.NETacross 16docs/files.
Changed
- Angular group bumped (13 package updates).
- Microsoft group bumped (10 package updates).
- Various CI action majors (checkout, setup-dotnet, login-action, metadata-action, setup-buildx-action, typescript/angular-eslint/dependabot-config housekeeping).
Full changelog: v2.6.0...v2.7.0
v2.6.0 — Security hardening & Poracle server management removal
Highlights
🔒 Security hardening
- Removed the Poracle server management feature (#176, PR #181) — eliminates the only shell-command-execution code path (
System.Diagnostics.Process+ SSH). Closes command-injection surface inRestartCommand/GroupMapPathconfig values and removes the need to mount SSH keys in the container.openssh-clientpackage removed from the Docker image. - Persisted ASP.NET Core DataProtection keys to filesystem (#174, PR #179) — keys now survive container restarts, eliminates ephemeral-key startup warnings.
✨ New
- Configurable login buttons (#172, PR #177) — show/hide Discord and Telegram login based on
.envconfig with admin-disabled state.
🔧 Changed
- Replaced AutoMapper with manual mapping extension methods (#173, PR #178) — removed the commercial-license dependency. All mappings now use static extension methods in
Core.Mappings. No API contract or behavioral changes.
🎨 Fixed
Upgrade notes
Backwards compatible
Existing .env files containing PORACLE_SERVER_* or SSH_KEY_PATH vars are silently ignored after upgrade — no .env changes required for the app to keep running.
Recommended ops actions
Since the SSH-based server restart feature was removed:
- Delete
./data/ssh_keyfrom the Docker host - Rotate the SSH keys previously used for PoracleJS server access
- Remove the PoracleWeb public key from
~/.ssh/authorized_keyson each PoracleJS server - Remove firewall rules opened for PoracleWeb → PoracleJS SSH access (port 22)
Full changelog: v2.5.0...v2.6.0
v2.5.0
What's New
Added
- Signup page redirect for non-registered users (#168, PR #171): New admin-configurable
signup_urlsite setting (Settings > Analytics & Links) displays a green "Sign Up" button on the login page. When a non-registered user attempts to log in and receives auser_not_registeredormissing_required_roleerror, the signup button directs them to the configured registration page. Button hidden when no URL is configured. - Internationalized ~45 hardcoded UI strings (PR #171): Extracted hardcoded English strings into i18n translation keys across all 11 supported languages covering auth errors, HTTP error toasts, test alert messages, admin settings, fort changes, max battles, and template condition labels.
Fixed
- Alarms landing on wrong profile after PoracleNG auto-switches via active_hours scheduler (#167, PR #170): JWT profile desync detected and auto-corrected via refreshed token on
/api/auth/me.
Changed
- Extracted
IJwtService: Consolidated JWT token generation from 4 independent implementations into a shared singleton.
Full Changelog: v2.4.1...v2.5.0
v2.4.1
Fixed
- Test alert DMs now satisfy the alarm's own filter (#165): Clicking Test Alert on a "Great League rank 1-1 Tinkaton" filter previously delivered a DM with IVs 1/14/14 at L25.5 — values that didn't satisfy the filter being tested — because
TestAlertService.BuildPokemonWebhookhardcoded IVs to 15/15/15, level to 35, CP to 2500, and shipped literalpvp_rankings_great_league/_ultra_leaguearrays withrank: 1regardless of the alarm's configured filter. PoracleNG's render pipeline then faithfully enriched the lies. Rewrote the monolithic builder as sixITestPayloadBuilderimplementations underCore.Services/TestAlerts/, each reading the alarm's own filter columns and emitting matching raw webhook values aligned to the live PGAN DTS template variable set. Ported gohbem's PVP rank calculator (Core.Services/Pvp/PvpRankCalculator+ 109-entryCpMultiplierTable) to resolve PVP-filter alarms to real rank-matching IV/level/CP combos — GL rank 1 Tinkaton now renders as 13/15/15 L49.5 CP 1498 with a matching single-entry PVP rank panel. Rank tables cached per(pokemonId, form, league)viaIPvpRankService+IMemoryCachewith no TTL (~16 KB per species, O(1) after the first ~1 ms sweep). ExtendedMasterDataServiceto expose per-speciesbaseAttack/baseDefense/baseStaminafrom the WatWowMap Masterfile. Every builder honors the full filter field set: IV floors/ceilings,min_iv/max_iv,min_level/max_level,min_cp/max_cp,form,gender,size, and league-aware PVP rank windows. Also aligned raid/egg wire fields to the live PGAN DTS convention (name+url, notgym_name/gym_url), mergedegginto theraidwire type (pokemon_id=0), fixed a latent quest-webhooktemplatefield collision with the in-game quest template key, and corrected invasion field names (name/incident_grunt_type/incident_expiration). 24 new payload assertion tests pin the filter-to-wire contract plus 19 PVP algorithm property tests. (PR #166) - custom geofence toggle not persisting (#163) (PR #164)
- Custom geofence toggle not persisting (#163): Activating/deactivating a user-drawn geofence via the Geofences page toggle appeared to succeed but silently reverted on the next page load. Root cause: PoracleNG's
POST /api/humans/{id}/setAreashandler intersects the submitted area list against fences whereuserSelectable=true(for non-admin users), and user geofences are served from PoracleWeb's feed withuserSelectable=falseso they were silently stripped. Regression introduced in v2.0.0 by the PoracleNG API proxy migration, which routed user geofence area writes throughSetAreasAsyncinstead of the pre-migration direct-DB path. IntroducedIUserAreaDualWriter— a tiny atomic-write abstraction overPoracleContextthat commits bothhumans.areaand the activeprofiles.areain a singleSaveChangesAsynccall (EF Core implicit transaction = the two writes cannot drift).UserGeofenceService.Create/Delete/AddToProfile/RemoveFromProfile/AdminDeletenow delegate to the writer instead of making separateIHumanRepository+IProfileRepositorycalls.AddToProfileandRemoveFromProfilealso callReloadGeofencesSafeAsyncmanually because the direct-DB path skips PoracleNG'sHandleSetAreasterminal reload. Also fixed a related bug where saving on the Areas page would silently strip user geofences the user had activated:AreaController.UpdateAreasnow callsIUserGeofenceService.PreserveOwnedAreasInHumanAsyncafterSetAreasAsync, which hands off to the writer's bulkAddAreasToActiveProfileAsyncmethod (one DB round-trip regardless of geofence count). This is a temporary workaround — the proper fix is a new PoracleNG API endpoint that bypasses theuserSelectablefilter for trusted callers. Seedocs/poracleng-enhancement-requests.md(trusted-set-areasgap). All affected sites are tagged withHACK: trusted-set-areascomments — when PoracleNG ships the fix,grep -rn "HACK: trusted-set-areas" --include="*.cs"will list every site that needs to be reverted.
v2.4.0 — Multi-language / i18n Support
What's New
Full internationalization support with 11 UI languages matching the original PoracleWeb PHP: English, French, German, Spanish, Dutch, Italian, Portuguese, Brazilian Portuguese, Polish, Danish, Swedish.
Highlights
- Runtime language switching — change language instantly from the user menu, no page reload
- Browser auto-detection — first visit auto-detects preferred language
- SVG country flags — crisp flag icons in the language selector
- Admin control —
allowed_languagessite setting restricts available languages - 12,331 translated strings — 1,121 keys per language across every page, dialog, tooltip, and snackbar
- Help guide fully translated — all 17 sections with rich HTML content
- Fallback detection — amber "English" chip on untranslated sections
- Shared pipes i18n — league, gender, lure, team, distance pipes use translated output
- Admin settings i18n — 106 setting labels and descriptions translatable
- I18nService tests — 11 unit tests for the core service
- MkDocs docs — i18n feature page + custom landing page
Upgrade Notes
- No database changes required
- New npm dependency:
@ngx-translate/corev17,@ngx-translate/http-loaderv17 - New assets:
src/assets/i18n/*.json(11 translation files),src/assets/flags/*.svg(11 flag icons) - Optional: Set
allowed_languagesin Admin → Settings → Features to restrict available languages
Full Changelog: v2.3.0...v2.4.0
v2.3.0
What's New
Added
- Area overview map on dashboard: Non-interactive Leaflet mini-map showing selected areas as color-coded polygons. Lazy-loaded via
@defer(on viewport)with zero additional API calls. Click navigates to the full Areas page. (#129, PR #160) - Profile active hours / schedule management UI (#158, PR #159)
- View and edit active hours on user profiles for automatic profile switching
- Schedule editor dialog with day picker, time picker, and weekly preview
- Amber schedule pills on profile cards showing activation times
- Location warning when profile has 0,0 coordinates (causes timezone bug in PoracleNG scheduler)
- Server-side validation (day 1-7, hours 0-23, mins 0-59, max 28 entries)
Removed
- Unused
ProfileListComponent(was never routed)
Full Changelog: v2.2.0...v2.3.0
v2.2.0
Added
- Max Battle (Dynamax) tracking alarms: Full-stack alarm module for Dynamax and Gigantamax Max Battle tracking at Power Spots (#118, PR #137)
- Fort Change tracking alarms: Monitor pokestop and gym changes — name changes, relocations, image updates, removals, and new forts (#119, PR #135)
- Unified Profiles page with alarm overview: View all alarms across all profiles with backup/restore, duplicate detection, and profile name uniqueness (#127, PR #147)
- Profile duplicate: Clone a profile with all alarms, areas, and location in one operation (#120, PR #145)
- Send test alert / notification preview: Test button on every alarm card sends a simulated notification through PoracleNG (#122, PR #148)
- Weather display at user location: Dashboard shows current in-game weather with boosted type chips and per-area weather icons (#128, PR #149)
- Pokemon availability indicators from Golbat: Pokemon selector shows which species are currently spawning with "Live > Spawning" filter (#133, PR #154)
- GeoJSON geofence export and import: Export/import geofences as standard GeoJSON files with drag-and-drop upload and preview (#130, PR #155)
- Max Battles quick pick support (#140, PR #143)
- Login method gating:
enable_discordandenable_telegramsettings gate login methods (#117, PR #136) - Dedicated Discord settings section in admin page (PR #116)
Fixed
- Quick pick "all invasions" fails with 400 (#139, PR #141)
- Raid card shows 9000 stars for level 9000 (#138, PR #144)
- Quick pick raid level override (PR #144)
- Profile rename not saving (PR #147)
- Quest test alerts missing static maps (PR #153)
Full Changelog: v2.1.3...v2.2.0
v2.1.3
Fixed
- Custom geofences lost when toggling areas:
syncSelectedFromAreas()was overwriting selected areas with only predefined names, silently dropping custom geofence subscriptions — toggling any predefined area checkbox then saving would deactivate custom geofences (#109, PR #113)
Full Changelog: v2.1.2...v2.1.3
v2.1.2
Fixed
- Areas page layout: Remove
max-width: 800pxconstraint for consistent full-width layout matching My Geofences page (#107, PR #110) - Site title after OAuth redirect: Load site settings after Discord callback stores JWT token — title was stuck on default "DM Alerts" because
loadOnce()fired before the token was available (#106, PR #111) - Webhook admin actions broken: Move user ID from path to query parameter for 8 admin endpoints — webhook IDs are URLs with slashes that broke ASP.NET Core
{id}route matching (#105, PR #112)
Full Changelog: v2.1.1...v2.1.2