at main
3 folders
16 files
fix(webhooks): send ntfy.sh URLs ntfy's native format
Our generic JSON envelope is unusable on ntfy. With a topic in the URL,
ntfy treats the whole request body as the message text, so subscribers
got a raw JSON dump instead of a notification — and once that body grows
past ~4 KB, which any alert with a few dozen matched auctions does, ntfy
converts it into a file attachment, so the notification only reads "You
received a file: attachment.json". Posting the envelope to an instance
root instead fails outright with `40009 topic invalid`, because ntfy's
own JSON publish format expects a `topic` field. Both routes were
reported by a user, and the settings copy has been advertising ntfy
support the whole time.
ntfy.sh URLs now get a plain-text body plus X-Title/X-Click/X-Tags
headers. Publishing that way needs no topic parsing, so every alias a
user might paste (`/topic`, `/topic/publish`) behaves identically, and
the body is a one-line summary that stays well clear of the attachment
threshold. Titles outside printable ASCII are RFC 2047-encoded, since
fetch rejects non-latin-1 header values and alert names are
user-supplied.
Detection is limited to the hosted service: self-hosted instances live
on arbitrary domains and can't be recognised from the URL alone, so they
keep receiving the JSON envelope. Non-ntfy endpoints are unchanged. The
settings Test button imports the same formatter the worker uses, so it
exercises the real wire format rather than a parallel one.
Verified against a local ntfy instance: title, click action, tags and
UTF-8 alert names all round-trip, with no attachment fallback.
fix(webhooks): send ntfy.sh URLs ntfy's native format
Our generic JSON envelope is unusable on ntfy. With a topic in the URL,
ntfy treats the whole request body as the message text, so subscribers
got a raw JSON dump instead of a notification — and once that body grows
past ~4 KB, which any alert with a few dozen matched auctions does, ntfy
converts it into a file attachment, so the notification only reads "You
received a file: attachment.json". Posting the envelope to an instance
root instead fails outright with `40009 topic invalid`, because ntfy's
own JSON publish format expects a `topic` field. Both routes were
reported by a user, and the settings copy has been advertising ntfy
support the whole time.
ntfy.sh URLs now get a plain-text body plus X-Title/X-Click/X-Tags
headers. Publishing that way needs no topic parsing, so every alias a
user might paste (`/topic`, `/topic/publish`) behaves identically, and
the body is a one-line summary that stays well clear of the attachment
threshold. Titles outside printable ASCII are RFC 2047-encoded, since
fetch rejects non-latin-1 header values and alert names are
user-supplied.
Detection is limited to the hosted service: self-hosted instances live
on arbitrary domains and can't be recognised from the URL alone, so they
keep receiving the JSON envelope. Non-ntfy endpoints are unchanged. The
settings Test button imports the same formatter the worker uses, so it
exercises the real wire format rather than a parallel one.
Verified against a local ntfy instance: title, click action, tags and
UTF-8 alert names all round-trip, with no attachment fallback.
fix(webhooks): send ntfy.sh URLs ntfy's native format
Our generic JSON envelope is unusable on ntfy. With a topic in the URL,
ntfy treats the whole request body as the message text, so subscribers
got a raw JSON dump instead of a notification — and once that body grows
past ~4 KB, which any alert with a few dozen matched auctions does, ntfy
converts it into a file attachment, so the notification only reads "You
received a file: attachment.json". Posting the envelope to an instance
root instead fails outright with `40009 topic invalid`, because ntfy's
own JSON publish format expects a `topic` field. Both routes were
reported by a user, and the settings copy has been advertising ntfy
support the whole time.
ntfy.sh URLs now get a plain-text body plus X-Title/X-Click/X-Tags
headers. Publishing that way needs no topic parsing, so every alias a
user might paste (`/topic`, `/topic/publish`) behaves identically, and
the body is a one-line summary that stays well clear of the attachment
threshold. Titles outside printable ASCII are RFC 2047-encoded, since
fetch rejects non-latin-1 header values and alert names are
user-supplied.
Detection is limited to the hosted service: self-hosted instances live
on arbitrary domains and can't be recognised from the URL alone, so they
keep receiving the JSON envelope. Non-ntfy endpoints are unchanged. The
settings Test button imports the same formatter the worker uses, so it
exercises the real wire format rather than a parallel one.
Verified against a local ntfy instance: title, click action, tags and
UTF-8 alert names all round-trip, with no attachment fallback.
refactor(filter-spec): make the shared package authoritative for disk geometry
packages/filter-spec/src/constants.ts documents itself as the fix for constants
that 'were previously re-declared in each place that needed them' and ends
'Import from here instead.' Three files did; six did not, including the two
matchers the package was built for and the ServerFilter.svelte sliders its own
docstring names.
Now imported rather than redeclared:
- ServerFilter.svelte: dropped its six local disk constants and the three
inlined copies of diskSizeCeiling(); DISK_UNIT_GB replaces the hardcoded 500
in the six getFormattedDiskSize calls.
- api/shared/filter-query.ts: 12 '* 500' -> DISK_UNIT_GB, local cityPrefixes ->
CITY_PREFIXES.
- worker/alert-matching-sql.ts: 24 inline 500s -> ${DISK_UNIT_GB}, and the
hardcoded IN ('FSN','NBG','HEL') is now built from CITY_PREFIXES.
- api/frontend/stats.ts: the prefix CASE and WHERE are generated from
CITY_PREFIXES instead of three hand-written SQL literals.
- lib/filter.ts: the Hetzner URL builder rounds by DISK_UNIT_GB.
Both 'deliberately dependency-free' headers are corrected: they were justifying
the bypass, when the package resolves from both toolchains — which is its whole
purpose.
auction-data-transformer's NBG/FSN check is deliberately left alone: it maps
datacenter to country, so coupling it to CITY_PREFIXES would break the moment a
city is added in a new country.
Also removes the write-only _hasStoredFilter, whose declaration went with the
constants block.
Verified by the conformance harness (DuckDB vs SQLite, 20 servers x 28 filters)
and the golden snapshot, both unmoved — the constants are numerically identical,
so this is a sourcing change with no behaviour change. Full check/lint/test green
on both workspaces.
fix(worker): fix the 26 production type errors and gate all of src in CI
worker/tsconfig.check.json gated 18 of 45 files. The 7 ungated production files
were the load-bearing ones — index, http-router, both Durable Objects,
alert-service, auction-service, analytics-query-service — so CI type-checked
everything except the code that fires alerts and imports data.
Root causes rather than symptoms:
- tsconfig targeted es2021, whose lib does not declare ErrorOptions, so every
'new Error(msg, { cause })' failed. Four errors from one config line; module
was already es2022 and workerd is well past it.
- Both DOs redeclared ctx/env on top of DurableObject's own protected members.
Parameterizing the generic (DurableObject<AuctionImportEnv>) lets the base
declare them at the right types. Six errors.
- http-router took triggerAuctionImport as () => Promise<unknown>, so the result
could not be spread and the failure could not be read. Typed against
AuctionImportResult, with isAuctionImportFailure to narrow the thrown value.
Two real bugs the checker had been catching:
- auction-service's no-valid-data path returned only 4 of the 9 declared fields,
so callers reading fetched/timestamp got undefined.
- the success path omitted timestamp entirely, despite computing it two lines
above and declaring it in the return type.
Both tests that asserted the incomplete shapes are updated — they had been
pinning the bug.
tsconfig.check.json now includes src/**/*.ts and excludes only src/__tests__,
so a new production file is gated by default instead of silently exempt. 26
files checked, up from 18. The ~236 remaining errors are all in test mocks and
stay tracked in the spec.
feat: enrich servers with CPU cores, threads, generation, and benchmark scores (#256)
* feat: enrich servers with CPU cores, threads, generation, and benchmark scores
Add CPU enrichment from Geekbench data for both auction and standard servers.
Enables filtering by core/thread count, sorting by CPU score, and displays
CPU details (cores/threads, architecture family, GB6 score) on server cards
and detail drawer.
- Add generate_cpu_specs.py to fetch and match Geekbench data
- Enrich auction servers in update_incremental.py via cpu-specs.json
- Enrich in CF Worker via AuctionDataTransformer.lookupCpuSpecs()
- D1 migration for cpu_cores, cpu_threads, cpu_generation, cpu_score columns
- Add cores/threads range sliders to filter UI
- Add CPU Score sort option
- Add CPU row to ServerFactSheet (both layouts)
- Add cpuCores/cpuThreads alert matching in MATCH_ALERTS_SQL
- Weekly GHA workflow to update cpu-specs.json from Geekbench
* fix: resolve CI failures for CPU enrichment
- Copy cpu-specs.json into worker/src/ instead of symlinking through
gitignored data/ directory
- Fix prettier formatting
* fix: resolve flaky Playwright tests
- landing.spec.ts: update text matchers to match current page content
("Dedicated Servers" not "Dedicated Server Auctions",
"check cloud availability" not "check cloud server availability")
- server-interactions.spec.ts: remove non-existent table lookup in
drawer, wait for loading state instead of arbitrary timeout
- advanced-filtering.spec.ts: replace networkidle + waitForTimeout with
waitForFilterUpdate helper that polls until count stabilizes
- fixtures.ts: add waitForFilterUpdate helper for reliable DuckDB waits
feat: enrich servers with CPU cores, threads, generation, and benchmark scores (#256)
* feat: enrich servers with CPU cores, threads, generation, and benchmark scores
Add CPU enrichment from Geekbench data for both auction and standard servers.
Enables filtering by core/thread count, sorting by CPU score, and displays
CPU details (cores/threads, architecture family, GB6 score) on server cards
and detail drawer.
- Add generate_cpu_specs.py to fetch and match Geekbench data
- Enrich auction servers in update_incremental.py via cpu-specs.json
- Enrich in CF Worker via AuctionDataTransformer.lookupCpuSpecs()
- D1 migration for cpu_cores, cpu_threads, cpu_generation, cpu_score columns
- Add cores/threads range sliders to filter UI
- Add CPU Score sort option
- Add CPU row to ServerFactSheet (both layouts)
- Add cpuCores/cpuThreads alert matching in MATCH_ALERTS_SQL
- Weekly GHA workflow to update cpu-specs.json from Geekbench
* fix: resolve CI failures for CPU enrichment
- Copy cpu-specs.json into worker/src/ instead of symlinking through
gitignored data/ directory
- Fix prettier formatting
* fix: resolve flaky Playwright tests
- landing.spec.ts: update text matchers to match current page content
("Dedicated Servers" not "Dedicated Server Auctions",
"check cloud availability" not "check cloud server availability")
- server-interactions.spec.ts: remove non-existent table lookup in
drawer, wait for loading state instead of arbitrary timeout
- advanced-filtering.spec.ts: replace networkidle + waitForTimeout with
waitForFilterUpdate helper that polls until count stabilizes
- fixtures.ts: add waitForFilterUpdate helper for reliable DuckDB waits
fix(worker): fix the 26 production type errors and gate all of src in CI
worker/tsconfig.check.json gated 18 of 45 files. The 7 ungated production files
were the load-bearing ones — index, http-router, both Durable Objects,
alert-service, auction-service, analytics-query-service — so CI type-checked
everything except the code that fires alerts and imports data.
Root causes rather than symptoms:
- tsconfig targeted es2021, whose lib does not declare ErrorOptions, so every
'new Error(msg, { cause })' failed. Four errors from one config line; module
was already es2022 and workerd is well past it.
- Both DOs redeclared ctx/env on top of DurableObject's own protected members.
Parameterizing the generic (DurableObject<AuctionImportEnv>) lets the base
declare them at the right types. Six errors.
- http-router took triggerAuctionImport as () => Promise<unknown>, so the result
could not be spread and the failure could not be read. Typed against
AuctionImportResult, with isAuctionImportFailure to narrow the thrown value.
Two real bugs the checker had been catching:
- auction-service's no-valid-data path returned only 4 of the 9 declared fields,
so callers reading fetched/timestamp got undefined.
- the success path omitted timestamp entirely, despite computing it two lines
above and declaring it in the return type.
Both tests that asserted the incomplete shapes are updated — they had been
pinning the bug.
tsconfig.check.json now includes src/**/*.ts and excludes only src/__tests__,
so a new production file is gated by default instead of silently exempt. 26
files checked, up from 18. The ~236 remaining errors are all in test mocks and
stay tracked in the spec.
fix(worker): fix the 26 production type errors and gate all of src in CI
worker/tsconfig.check.json gated 18 of 45 files. The 7 ungated production files
were the load-bearing ones — index, http-router, both Durable Objects,
alert-service, auction-service, analytics-query-service — so CI type-checked
everything except the code that fires alerts and imports data.
Root causes rather than symptoms:
- tsconfig targeted es2021, whose lib does not declare ErrorOptions, so every
'new Error(msg, { cause })' failed. Four errors from one config line; module
was already es2022 and workerd is well past it.
- Both DOs redeclared ctx/env on top of DurableObject's own protected members.
Parameterizing the generic (DurableObject<AuctionImportEnv>) lets the base
declare them at the right types. Six errors.
- http-router took triggerAuctionImport as () => Promise<unknown>, so the result
could not be spread and the failure could not be read. Typed against
AuctionImportResult, with isAuctionImportFailure to narrow the thrown value.
Two real bugs the checker had been catching:
- auction-service's no-valid-data path returned only 4 of the 9 declared fields,
so callers reading fetched/timestamp got undefined.
- the success path omitted timestamp entirely, despite computing it two lines
above and declaring it in the return type.
Both tests that asserted the incomplete shapes are updated — they had been
pinning the bug.
tsconfig.check.json now includes src/**/*.ts and excludes only src/__tests__,
so a new production file is gated by default instead of silently exempt. 26
files checked, up from 18. The ~236 remaining errors are all in test mocks and
stay tracked in the spec.
feat: enrich servers with CPU cores, threads, generation, and benchmark scores (#256)
* feat: enrich servers with CPU cores, threads, generation, and benchmark scores
Add CPU enrichment from Geekbench data for both auction and standard servers.
Enables filtering by core/thread count, sorting by CPU score, and displays
CPU details (cores/threads, architecture family, GB6 score) on server cards
and detail drawer.
- Add generate_cpu_specs.py to fetch and match Geekbench data
- Enrich auction servers in update_incremental.py via cpu-specs.json
- Enrich in CF Worker via AuctionDataTransformer.lookupCpuSpecs()
- D1 migration for cpu_cores, cpu_threads, cpu_generation, cpu_score columns
- Add cores/threads range sliders to filter UI
- Add CPU Score sort option
- Add CPU row to ServerFactSheet (both layouts)
- Add cpuCores/cpuThreads alert matching in MATCH_ALERTS_SQL
- Weekly GHA workflow to update cpu-specs.json from Geekbench
* fix: resolve CI failures for CPU enrichment
- Copy cpu-specs.json into worker/src/ instead of symlinking through
gitignored data/ directory
- Fix prettier formatting
* fix: resolve flaky Playwright tests
- landing.spec.ts: update text matchers to match current page content
("Dedicated Servers" not "Dedicated Server Auctions",
"check cloud availability" not "check cloud server availability")
- server-interactions.spec.ts: remove non-existent table lookup in
drawer, wait for loading state instead of arbitrary timeout
- advanced-filtering.spec.ts: replace networkidle + waitForTimeout with
waitForFilterUpdate helper that polls until count stabilizes
- fixtures.ts: add waitForFilterUpdate helper for reliable DuckDB waits
fix(worker): fix the 26 production type errors and gate all of src in CI
worker/tsconfig.check.json gated 18 of 45 files. The 7 ungated production files
were the load-bearing ones — index, http-router, both Durable Objects,
alert-service, auction-service, analytics-query-service — so CI type-checked
everything except the code that fires alerts and imports data.
Root causes rather than symptoms:
- tsconfig targeted es2021, whose lib does not declare ErrorOptions, so every
'new Error(msg, { cause })' failed. Four errors from one config line; module
was already es2022 and workerd is well past it.
- Both DOs redeclared ctx/env on top of DurableObject's own protected members.
Parameterizing the generic (DurableObject<AuctionImportEnv>) lets the base
declare them at the right types. Six errors.
- http-router took triggerAuctionImport as () => Promise<unknown>, so the result
could not be spread and the failure could not be read. Typed against
AuctionImportResult, with isAuctionImportFailure to narrow the thrown value.
Two real bugs the checker had been catching:
- auction-service's no-valid-data path returned only 4 of the 9 declared fields,
so callers reading fetched/timestamp got undefined.
- the success path omitted timestamp entirely, despite computing it two lines
above and declaring it in the return type.
Both tests that asserted the incomplete shapes are updated — they had been
pinning the bug.
tsconfig.check.json now includes src/**/*.ts and excludes only src/__tests__,
so a new production file is gated by default instead of silently exempt. 26
files checked, up from 18. The ~236 remaining errors are all in test mocks and
stay tracked in the spec.