From 74245ee07d7e1c7d1c4a23dfe69a1a1bd9ff4a35 Mon Sep 17 00:00:00 2001 From: NeilClack Date: Mon, 17 Aug 2026 20:36:59 -0500 Subject: [PATCH] Remove API key requirement --- .gitea/workflows/build.yml | 13 +------- AGENTS.md | 30 ++++------------- CMakeLists.txt | 15 --------- README.md | 54 +++++++------------------------ flake.nix | 33 ++++++++----------- nix/package.nix | 8 +---- src/license_key_page.cpp | 2 +- src/network/api_client.cpp | 39 ++++++++-------------- src/network/api_client.h | 21 ++++-------- src/network/license_service.cpp | 3 +- src/network/license_service.h | 1 - src/network/proclamation_sync.cpp | 10 +++--- src/network/proclamation_sync.h | 6 ++-- tests/test_api_client.cpp | 23 ++++++------- tests/test_color_palettes.cpp | 2 +- tests/test_license_service.cpp | 8 ++--- 16 files changed, 74 insertions(+), 194 deletions(-) diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml index f740d5c..d139054 100644 --- a/.gitea/workflows/build.yml +++ b/.gitea/workflows/build.yml @@ -4,7 +4,6 @@ on: # Manual only for now. Select the branch to build when dispatching: # main -> APP_VERSION is the current git tag # dev -> APP_VERSION is the current commit hash - # Both branches bake the portal API key from secrets. workflow_dispatch: jobs: @@ -44,28 +43,18 @@ jobs: else APP_VERSION="$(git rev-parse --short HEAD)" fi - echo "APP_VERSION=$APP_VERSION" >> "$GITHUB_ENV" echo "Branch: ${{ gitea.ref_name }} -> APP_VERSION=$APP_VERSION" - if [ -n "${{ secrets.RWT_API_KEY }}" ]; then - echo "API key: provided via secret RWT_API_KEY" - else - echo "API key: NOT set (build proceeds without an Authorization header)" - fi # checks is keyed per-system (checks..default); nix build's # #attr shorthand can't resolve it, so resolve the current system inline. - # --impure lets the flake read APP_VERSION/API_KEY via builtins.getEnv + # --impure lets the flake read APP_VERSION via builtins.getEnv # (pure evals always get the package defaults). The offline test suite # runs in the package's checkPhase. - name: Build + offline test suite - env: - API_KEY: ${{ secrets.RWT_API_KEY }} run: nix build -L ".#checks.$(nix eval --impure --raw --expr 'builtins.currentSystem').default" --impure - name: Live portal tests - if: ${{ secrets.RWT_LIVE_API_KEY != '' }} env: - RWT_LIVE_API_KEY: ${{ secrets.RWT_LIVE_API_KEY }} RWT_LIVE_ACCOUNT_EMAIL: ${{ secrets.RWT_LIVE_ACCOUNT_EMAIL }} RWT_LIVE_LICENSE_EMAIL: ${{ secrets.RWT_LIVE_LICENSE_EMAIL }} RWT_LIVE_LICENSE_KEY: ${{ secrets.RWT_LIVE_LICENSE_KEY }} diff --git a/AGENTS.md b/AGENTS.md index ac4e5d2..b3b1b2e 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -33,23 +33,8 @@ cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Debug -DAPP_VERSION="1.2.3" The Nix package bakes `-DAPP_VERSION=` via `cmakeFlags` (`version` defaults to `0.1.0` in `nix/package.nix`; the flake overrides it from the `APP_VERSION` env var on impure evals). `nix develop` -sets `APP_VERSION=dev` and exports `API_KEY` from the ambient environment -(falling back to `RWT_LIVE_API_KEY`). CMake seeds a *fresh* configure from the -`APP_VERSION` / `API_KEY` env vars; `-D` wins on reconfigure. - -The portal API key is also a build-time argument: `-DAPI_KEY="..."` (CMake) or -`apiKey` on `callPackage` (Nix). It is compiled into the binary as the -`Authorization: Bearer` token and defaults to empty (no auth header). Keep the -real value in CI secrets, never in source — rotate by rebuilding with a new -key. - -Version and API key can be baked in together with a single configure: - -```sh -cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Debug \ - -DAPP_VERSION="1.2.3" \ - -DAPI_KEY="$API_KEY" -``` +sets `APP_VERSION=dev`. CMake seeds a *fresh* configure from the +`APP_VERSION` env var; `-D` wins on reconfigure. - The app resolves `assets/...` relative to its working directory — launch `./build/radiantwave` from the repo root. (The Nix wrapper chdirs to @@ -238,14 +223,11 @@ cached. `checks` is keyed per-system, so the workflow resolves the current system via `nix eval --impure --expr 'builtins.currentSystem'`). The build bakes in a per-branch `APP_VERSION` — the current git tag on `main`, the current - commit hash on `dev` — plus the portal API key from the `RWT_API_KEY` - secret. The flake reads `APP_VERSION`/`API_KEY` from the environment via + commit hash on `dev`. The flake reads `APP_VERSION` from the environment via `builtins.getEnv`, so the build must pass `--impure` (pure evals get the - package defaults). When the `RWT_LIVE_API_KEY` secret is set it also runs - `test_api_client` / `test_license_service` against the portal via - `nix develop` (outside the sandbox, like a dev machine). Those live tests - also need `RWT_LIVE_ACCOUNT_EMAIL`, `RWT_LIVE_LICENSE_EMAIL`, and - `RWT_LIVE_LICENSE_KEY`. + package defaults). The live portal tests run from `nix develop` when the + `RWT_LIVE_*` secrets are set and need `RWT_LIVE_ACCOUNT_EMAIL`, + `RWT_LIVE_LICENSE_EMAIL`, and `RWT_LIVE_LICENSE_KEY`. - `test_assets` must pass before committing — it catches missing fonts/images that break the app at runtime. - `cmake --build build` builds everything; pass `--target ` to build one diff --git a/CMakeLists.txt b/CMakeLists.txt index 1a4df17..244d966 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,20 +18,6 @@ else() "Version string shown in the Home footer (override at configure time)") endif() -# API bearer key baked into the binary and sent as `Authorization: Bearer ` -# on every portal request. Leave empty to send no auth header. Never commit the -# real value — inject it at CI build time from a secret, e.g.: -# cmake ... -DAPI_KEY="${{ secrets.PORTAL_API_KEY }}" -# The `nix develop` shell exports API_KEY from the ambient environment, which -# seeds a fresh configure; -D wins on reconfigure. -if(DEFINED ENV{API_KEY}) - set(API_KEY "$ENV{API_KEY}" CACHE STRING - "Portal API key baked into the binary (leave empty to skip auth)") -else() - set(API_KEY "" CACHE STRING - "Portal API key baked into the binary (leave empty to skip auth)") -endif() - find_package(SDL3 REQUIRED) find_package(SDL3_ttf REQUIRED) find_package(SDL3_image REQUIRED) @@ -90,7 +76,6 @@ add_executable(radiantwave target_compile_definitions(radiantwave PRIVATE APP_VERSION="${APP_VERSION}" - API_KEY="${API_KEY}" ) target_include_directories(radiantwave PRIVATE src) diff --git a/README.md b/README.md index b3b8c5e..a5471c0 100644 --- a/README.md +++ b/README.md @@ -136,45 +136,13 @@ If `APP_VERSION` is left unset the footer falls back to the project version from `CMakeLists.txt` (`project(... VERSION ...)`); a build that defines no version at all shows `unknown`. The Nix build sets `APP_VERSION` automatically to the flake package version, so a plain `nix build` needs no extra flags. -With `--impure` the flake instead reads the `APP_VERSION` / `API_KEY` -environment variables — this is how CI injects the version and portal key +With `--impure` the flake instead reads the `APP_VERSION` +environment variable — this is how CI injects the version (see the CI note under *Common Pitfalls*). Pure evals always get the package defaults, because `builtins.getEnv` cannot see the environment in pure evaluation mode. -#### Portal API key - -Every portal request sends `Authorization: Bearer `. The key is baked -into the binary at build time via the `API_KEY` CMake cache variable. It is -**optional** — when unset, requests go out without an auth header. Keep the -real value out of the source tree and inject it at build time from your CI -secret store so it never lands in `git`: - -```sh -cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release \ - -DAPI_KEY="${{ secrets.PORTAL_API_KEY }}" -cmake --build build -``` - -Rotate the key by building with a new value and shipping the new binary. The -Nix package accepts the same value via `callPackage ./nix/package.nix { -apiKey = "…"; }`, or via the `API_KEY` environment variable with -`nix build --impure`; the flake itself never embeds one. - -#### Version + API key in one configure - -`APP_VERSION` and `API_KEY` are independent CMake cache variables, so pass -both — plus the build type — in a single configure: - -```sh -cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Debug \ - -DAPP_VERSION="1.2.3" \ - -DAPI_KEY="${{ secrets.PORTAL_API_KEY }}" -cmake --build build -``` - -`-D` values always win over the `APP_VERSION` / `API_KEY` environment -variables, which `nix develop` seeds from the ambient environment. +#### Version in one configure ### Building with Nix @@ -187,14 +155,14 @@ nix build .#packages.x86_64-linux.default # needs a Linux machine/builders nix build .#packages.aarch64-linux.default ``` -To bake in a custom version and/or portal key, build impurely — the flake -reads `APP_VERSION` and `API_KEY` from the environment on impure evals: +To bake in a custom version, build impurely — the flake +reads `APP_VERSION` from the environment on impure evals: ```sh -APP_VERSION="1.2.3" API_KEY="$PORTAL_API_KEY" nix build --impure .#default +APP_VERSION="1.2.3" nix build --impure .#default ``` -A pure `nix build` always uses the package defaults (`0.1.0`, no auth header) +A pure `nix build` always uses the package defaults (`0.1.0`) even if those variables are set in your shell, because pure evaluation cannot see the environment. CI uses `--impure` to inject them. @@ -633,9 +601,9 @@ status to decide which page to show. workflow is `.gitea/workflows/build.yml`. It is **manual-only** (`workflow_dispatch`) — pick the branch when dispatching. `main` builds bake the current git tag as `APP_VERSION`; `dev` builds bake the current commit - hash. Both pass the portal key from the `RWT_API_KEY` secret and build with - `nix build --impure` (which lets the flake's env injection work). The - offline test suite runs in the package's `checkPhase`; the live portal tests - run from `nix develop` when the `RWT_LIVE_*` secrets are set. + hash. Both build with `nix build --impure` (which lets the flake's env + injection work). The offline test suite runs in the package's `checkPhase`; + the live portal tests run from `nix develop` when the `RWT_LIVE_*` secrets + are set. - Asset tests (`test_assets`) must pass before committing -- they catch missing fonts and images that would break the app at runtime. diff --git a/flake.nix b/flake.nix index b2abc88..0497808 100644 --- a/flake.nix +++ b/flake.nix @@ -34,18 +34,16 @@ }; in { - # Version and API key are injected from the ambient environment on - # impure evals (CI sets APP_VERSION/API_KEY from the git ref and a - # secret). Pure evals (`nix flake check`, local `nix build`) can't - # see the environment, so builtins.getEnv returns "" and the - # package falls back to its 0.1.0 default / no auth header. + # Version is injected from the ambient environment on + # impure evals (CI sets APP_VERSION from the git ref). Pure evals + # (`nix flake check`, local `nix build`) can't see the + # environment, so builtins.getEnv returns "" and the package + # falls back to its 0.1.0 default. packages.default = let envVersion = builtins.getEnv "APP_VERSION"; - envApiKey = builtins.getEnv "API_KEY"; packageArgs = - { apiKey = envApiKey; } - // (nixpkgs.lib.optionalAttrs (envVersion != "") { version = envVersion; }); + nixpkgs.lib.optionalAttrs (envVersion != "") { version = envVersion; }; in pkgs.callPackage ./nix/package.nix packageArgs; @@ -53,22 +51,17 @@ inputsFrom = [ self.packages.${system}.default ]; packages = [ ]; env = { - # APP_VERSION and API_KEY are CMake cache variables with no - # default; on a fresh configure cmake seeds their cache from - # environment variables of the same name, so a plain - # `cmake -S . -B build` bakes them into the binary. + # APP_VERSION is a CMake cache variable with no default; on a + # fresh configure cmake seeds its cache from the environment + # variable of the same name, so a plain `cmake -S . -B build` + # bakes it into the binary. APP_VERSION = "dev"; }; shellHook = '' - # Never commit the real key. Export API_KEY (or the CI name - # RWT_LIVE_API_KEY) in your ambient environment and this shell - # picks it up; empty by default (no auth header). - export API_KEY="''${API_KEY:-''${RWT_LIVE_API_KEY:-}}" - - echo "RadiantWave dev shell (APP_VERSION=dev, API_KEY=$([[ -n "$API_KEY" ]] && echo set || echo unset))" + echo "RadiantWave dev shell (APP_VERSION=dev)" echo " Configure: cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Debug" - echo " Bake version+key: cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Debug -DAPP_VERSION=\"1.2.3\" -DAPI_KEY=\"\"" - echo " Nix build (env version+key, impure): APP_VERSION=... API_KEY=... nix build --impure .#default" + echo " Bake version: cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Debug -DAPP_VERSION=\"1.2.3\"" + echo " Nix build (env version, impure): APP_VERSION=... nix build --impure .#default" echo " Build: cmake --build build" echo " Run: ./build/radiantwave" echo " Tests: ./build/test_assets && ./build/test_" diff --git a/nix/package.nix b/nix/package.nix index d4e4c0f..c61a304 100644 --- a/nix/package.nix +++ b/nix/package.nix @@ -15,9 +15,6 @@ curl, openssl, systemdLibs, - # Portal API key baked into the binary. Keep it out of the flake; pass it - # when calling callPackage from CI/your own config (see README "App version"). - apiKey ? "", # App version baked into the Home footer's Version row. CI overrides this # per branch (git tag on main, commit hash on dev); local/pure builds keep # the 0.1.0 default. @@ -54,10 +51,7 @@ stdenv.mkDerivation (finalAttrs: { # Bake the package version into the binary so the Home footer's Version row # shows it (CMake passes APP_VERSION through as a compile definition). - # API_KEY is only added when a non-empty key is supplied to callPackage. - cmakeFlags = - [ "-DAPP_VERSION=${finalAttrs.version}" ] - ++ lib.optionals (apiKey != "") [ "-DAPI_KEY=${apiKey}" ]; + cmakeFlags = [ "-DAPP_VERSION=${finalAttrs.version}" ]; # gtest is a buildInput, not native: nixpkgs builds it shared, so the # check-phase test binaries need its lib dir baked into their RPATH. diff --git a/src/license_key_page.cpp b/src/license_key_page.cpp index f9dc98d..750fac5 100644 --- a/src/license_key_page.cpp +++ b/src/license_key_page.cpp @@ -97,7 +97,7 @@ void LicenseKeyPage::submit() // When the email is missing the key is saved but the check is skipped. std::string errorMessage; const radiantwave::license::Result result = radiantwave::license::validateStoredLicense( - state_->db, "", &errorMessage); + state_->db, &errorMessage); switch (result) { case radiantwave::license::Result::Ok: diff --git a/src/network/api_client.cpp b/src/network/api_client.cpp index cc314ca..6a7f621 100644 --- a/src/network/api_client.cpp +++ b/src/network/api_client.cpp @@ -4,10 +4,6 @@ #include #include -#ifndef API_KEY -#define API_KEY "" -#endif - namespace radiantwave::api { namespace { @@ -31,7 +27,7 @@ size_t writeCallback(char* ptr, size_t size, size_t nmemb, void* userdata) { } // Shared curl setup + perform. Returns a populated HttpResponse. -HttpResponse performRequest(const std::string& url, const std::string& apiKey, +HttpResponse performRequest(const std::string& url, bool post, const std::string& jsonBody) { HttpResponse response; @@ -47,12 +43,6 @@ HttpResponse performRequest(const std::string& url, const std::string& apiKey, curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response.body); struct curl_slist* headers = nullptr; - // An explicit apiKey wins; otherwise fall back to the key baked in at - // build time via -DAPI_KEY (empty string when unset). - const std::string effectiveKey = apiKey.empty() ? API_KEY : apiKey; - if (!effectiveKey.empty()) { - headers = curl_slist_append(headers, ("Authorization: Bearer " + effectiveKey).c_str()); - } if (post) { headers = curl_slist_append(headers, "Content-Type: application/json"); curl_easy_setopt(curl, CURLOPT_POST, 1L); @@ -79,13 +69,12 @@ HttpResponse performRequest(const std::string& url, const std::string& apiKey, // ─── HTTP helpers ────────────────────────────────────────────────────── -HttpResponse httpGet(const std::string& path, const std::string& apiKey) { - return performRequest(urlFor(path), apiKey, false, ""); +HttpResponse httpGet(const std::string& path) { + return performRequest(urlFor(path), false, ""); } -HttpResponse httpPost(const std::string& path, const std::string& jsonBody, - const std::string& apiKey) { - return performRequest(urlFor(path), apiKey, true, jsonBody); +HttpResponse httpPost(const std::string& path, const std::string& jsonBody) { + return performRequest(urlFor(path), true, jsonBody); } // ─── GET /frequencies ────────────────────────────────────────────────── @@ -112,8 +101,8 @@ bool parseFrequenciesJson(const std::string& body, std::vector& out) return true; } -bool fetchFrequencies(std::vector& out, const std::string& apiKey) { - HttpResponse response = httpGet(PATH_FREQUENCIES, apiKey); +bool fetchFrequencies(std::vector& out) { + HttpResponse response = httpGet(PATH_FREQUENCIES); if (!response.transportOk || response.status < 200 || response.status >= 300) { return false; } @@ -169,8 +158,8 @@ bool parseColorPalettesJson(const std::string& body, std::vector& return true; } -bool fetchColorPalettes(std::vector& out, const std::string& apiKey) { - HttpResponse response = httpGet(PATH_COLOR_PALETTES, apiKey); +bool fetchColorPalettes(std::vector& out) { + HttpResponse response = httpGet(PATH_COLOR_PALETTES); if (!response.transportOk || response.status < 200 || response.status >= 300) { return false; } @@ -196,13 +185,12 @@ bool parseLicenseJson(const std::string& body, LicenseInfo& out) { return !out.key.empty(); } -LicenseResult validateLicense(const std::string& key, const std::string& email, - const std::string& apiKey) { +LicenseResult validateLicense(const std::string& key, const std::string& email) { LicenseResult result; const std::string jsonBody = "{\"license_key\":\"" + key + "\",\"email_address\":\"" + email + "\"}"; - HttpResponse response = httpPost(PATH_LICENSE, jsonBody, apiKey); + HttpResponse response = httpPost(PATH_LICENSE, jsonBody); result.status = response.status; if (!response.transportOk) { @@ -233,8 +221,7 @@ bool parseCreateAccountJson(const std::string& body, AccountBody& out) { return true; } -AccountResult createAccount(const std::string& email, const std::string& name, - const std::string& apiKey) { +AccountResult createAccount(const std::string& email, const std::string& name) { AccountResult result; std::string jsonBody = @@ -244,7 +231,7 @@ AccountResult createAccount(const std::string& email, const std::string& name, } jsonBody += "}"; - HttpResponse response = httpPost(PATH_CREATE_ACCOUNT, jsonBody, apiKey); + HttpResponse response = httpPost(PATH_CREATE_ACCOUNT, jsonBody); result.status = response.status; if (!response.transportOk) { diff --git a/src/network/api_client.h b/src/network/api_client.h index c3000d7..bb68d19 100644 --- a/src/network/api_client.h +++ b/src/network/api_client.h @@ -24,14 +24,9 @@ struct HttpResponse { bool transportOk = false; // true when the request completed without a transport error }; -// Performs a GET/POST against `path` (relative to API_BASE_URL). When `apiKey` is -// non-empty it is sent as `Authorization: Bearer `. When it is empty (or -// omitted) the request falls back to the key baked in at build time via the -// `API_KEY` compile definition (see CMakeLists.txt); with neither set, no auth -// header is sent. -HttpResponse httpGet(const std::string& path, const std::string& apiKey = ""); -HttpResponse httpPost(const std::string& path, const std::string& jsonBody, - const std::string& apiKey = ""); +// Performs a GET/POST against `path` (relative to API_BASE_URL). +HttpResponse httpGet(const std::string& path); +HttpResponse httpPost(const std::string& path, const std::string& jsonBody); // ─── GET /frequencies ────────────────────────────────────────────────── @@ -45,7 +40,7 @@ struct Frequency { bool parseFrequenciesJson(const std::string& body, std::vector& out); // Fetch the solfeggio frequencies. Returns true only on HTTP 2xx + successful parse. -bool fetchFrequencies(std::vector& out, const std::string& apiKey = ""); +bool fetchFrequencies(std::vector& out); // ─── GET /color-palettes ─────────────────────────────────────────────── @@ -75,7 +70,7 @@ bool parseColorPalettesJson(const std::string& body, std::vector& // NOTE: the portal does not expose this route yet. This is readiness code — it // is deliberately not called anywhere in the app until the route ships, so // palettes can be updated from the portal rather than code releases. -bool fetchColorPalettes(std::vector& out, const std::string& apiKey = ""); +bool fetchColorPalettes(std::vector& out); // ─── POST /license ───────────────────────────────────────────────────── @@ -98,8 +93,7 @@ struct LicenseResult { // Validate a license key / email pair against the portal. Never throws; failures // (network, HTTP error status) are reported through the returned LicenseResult. // A 2xx response means the license is valid and the subscription is active. -LicenseResult validateLicense(const std::string& key, const std::string& email, - const std::string& apiKey = ""); +LicenseResult validateLicense(const std::string& key, const std::string& email); // ─── POST /automations/create/account ────────────────────────────────── @@ -122,7 +116,6 @@ struct AccountResult { // Creates (or reuses) a user account and issues a license key. The key is emailed // to the user and is NOT part of the response. `name` may be empty. -AccountResult createAccount(const std::string& email, const std::string& name = "", - const std::string& apiKey = ""); +AccountResult createAccount(const std::string& email, const std::string& name = ""); } // namespace radiantwave::api diff --git a/src/network/license_service.cpp b/src/network/license_service.cpp index 56b3d39..a812893 100644 --- a/src/network/license_service.cpp +++ b/src/network/license_service.cpp @@ -17,7 +17,6 @@ long long nowEpochSeconds() { } // namespace Result validateStoredLicense(radiantwave::db::DatabaseManager& db, - const std::string& apiKey, std::string* outErrorMessage) { if (outErrorMessage) outErrorMessage->clear(); @@ -28,7 +27,7 @@ Result validateStoredLicense(radiantwave::db::DatabaseManager& db, } const radiantwave::api::LicenseResult result = - radiantwave::api::validateLicense(key, email, apiKey); + radiantwave::api::validateLicense(key, email); if (result.status == 0) { if (outErrorMessage) *outErrorMessage = "network error"; diff --git a/src/network/license_service.h b/src/network/license_service.h index 0fa2317..2888118 100644 --- a/src/network/license_service.h +++ b/src/network/license_service.h @@ -26,7 +26,6 @@ constexpr long long GRACE_PERIOD_SECONDS = 7LL * 24 * 60 * 60; // // `outErrorMessage` is an optional out-param. Result validateStoredLicense(radiantwave::db::DatabaseManager& db, - const std::string& apiKey = "", std::string* outErrorMessage = nullptr); // Records the current time as the "last verified" stamp of the license. Called diff --git a/src/network/proclamation_sync.cpp b/src/network/proclamation_sync.cpp index 4cde51d..e0b6452 100644 --- a/src/network/proclamation_sync.cpp +++ b/src/network/proclamation_sync.cpp @@ -190,10 +190,9 @@ bool parseProclamationPalettesJson(const std::string& body, return true; } -bool syncColorPalettesFromApi(radiantwave::db::DatabaseManager& db, - const std::string& apiKey) { +bool syncColorPalettesFromApi(radiantwave::db::DatabaseManager& db) { std::vector palettes; - if (!api::fetchColorPalettes(palettes, apiKey)) { + if (!api::fetchColorPalettes(palettes)) { return false; } @@ -211,9 +210,8 @@ bool syncColorPalettesFromApi(radiantwave::db::DatabaseManager& db, return db.replaceColorPalettes(records); } -bool syncProclamationPalettesFromApi(radiantwave::db::DatabaseManager& db, - const std::string& apiKey) { - api::HttpResponse response = api::httpGet(api::PATH_PROCLAMATIONS, apiKey); +bool syncProclamationPalettesFromApi(radiantwave::db::DatabaseManager& db) { + api::HttpResponse response = api::httpGet(api::PATH_PROCLAMATIONS); if (!response.transportOk || response.status < 200 || response.status >= 300) { return false; } diff --git a/src/network/proclamation_sync.h b/src/network/proclamation_sync.h index de1781f..1c47612 100644 --- a/src/network/proclamation_sync.h +++ b/src/network/proclamation_sync.h @@ -37,13 +37,11 @@ bool parseProclamationPalettesJson(const std::string& body, // NOTE: not yet wired into the app flow — the portal does not expose the route // yet. Readiness code: palettes should be updated from the portal, not code // releases, so callers should treat this as a future sync step. -bool syncColorPalettesFromApi(radiantwave::db::DatabaseManager& db, - const std::string& apiKey = ""); +bool syncColorPalettesFromApi(radiantwave::db::DatabaseManager& db); // Alternative to syncColorPalettesFromApi: fetch proclamations and replace the // local palette table using the palettes embedded in each proclamation. // NOTE: not yet wired into the app flow; see syncColorPalettesFromApi. -bool syncProclamationPalettesFromApi(radiantwave::db::DatabaseManager& db, - const std::string& apiKey = ""); +bool syncProclamationPalettesFromApi(radiantwave::db::DatabaseManager& db); } // namespace radiantwave::sync diff --git a/tests/test_api_client.cpp b/tests/test_api_client.cpp index a89f135..5684e21 100644 --- a/tests/test_api_client.cpp +++ b/tests/test_api_client.cpp @@ -19,11 +19,6 @@ std::string env(const char* name) { return value ? std::string(value) : std::string(); } -// Returns the optional live API key, or "" when none was provided. -std::string liveApiKey() { - return env("RW_LIVE_API_KEY"); -} - } // namespace // ─── Offline: parse functions ───────────────────────────────────────── @@ -163,7 +158,7 @@ TEST(ApiClientTest, ParseCategoriesHandlesNullParentId) { TEST(ApiClientLiveTest, FetchFrequenciesLive) { std::vector out; - EXPECT_TRUE(fetchFrequencies(out, liveApiKey())); + EXPECT_TRUE(fetchFrequencies(out)); ASSERT_GE(out.size(), 7u); bool foundSolarPlexus = false; @@ -180,7 +175,7 @@ TEST(ApiClientLiveTest, FetchFrequenciesLive) { // ─── Live: proclamation + category parsers against the real API ──────── TEST(ApiClientLiveTest, ProclamationsAndCategoriesLiveSmoke) { - HttpResponse procsResponse = httpGet(PATH_PROCLAMATIONS, liveApiKey()); + HttpResponse procsResponse = httpGet(PATH_PROCLAMATIONS); ASSERT_TRUE(procsResponse.transportOk); ASSERT_EQ(procsResponse.status, 200); @@ -193,7 +188,7 @@ TEST(ApiClientLiveTest, ProclamationsAndCategoriesLiveSmoke) { EXPECT_FALSE(proclamations[0].id.empty()); } - HttpResponse catsResponse = httpGet(PATH_CATEGORIES, liveApiKey()); + HttpResponse catsResponse = httpGet(PATH_CATEGORIES); ASSERT_TRUE(catsResponse.transportOk); ASSERT_EQ(catsResponse.status, 200); @@ -205,14 +200,14 @@ TEST(ApiClientLiveTest, ProclamationsAndCategoriesLiveSmoke) { // ─── Live: POST /license error paths (no credentials required) ───────── TEST(ApiClientLiveTest, ValidateLicenseMissingKeyLive) { - LicenseResult result = validateLicense("", "test@example.com", liveApiKey()); + LicenseResult result = validateLicense("", "test@example.com"); ASSERT_FALSE(result.ok()); EXPECT_EQ(result.status, 400); EXPECT_EQ(result.error_message, "license_key is required"); } TEST(ApiClientLiveTest, ValidateLicenseMissingEmailLive) { - LicenseResult result = validateLicense("AAAA-AAAA-AAAA-AAAA", "", liveApiKey()); + LicenseResult result = validateLicense("AAAA-AAAA-AAAA-AAAA", ""); ASSERT_FALSE(result.ok()); EXPECT_EQ(result.status, 400); EXPECT_EQ(result.error_message, "email_address is required"); @@ -220,7 +215,7 @@ TEST(ApiClientLiveTest, ValidateLicenseMissingEmailLive) { TEST(ApiClientLiveTest, ValidateLicenseUnknownKeyLive) { LicenseResult result = - validateLicense("AAAA-AAAA-AAAA-AAAA", "nobody@example.com", liveApiKey()); + validateLicense("AAAA-AAAA-AAAA-AAAA", "nobody@example.com"); ASSERT_FALSE(result.ok()); EXPECT_EQ(result.status, 400); EXPECT_EQ(result.error_message, "license key not found"); @@ -235,7 +230,7 @@ TEST(ApiClientLiveTest, ValidateLicenseSuccessLive) { GTEST_SKIP() << "RW_LIVE_LICENSE_KEY / RW_LIVE_LICENSE_EMAIL not set"; } - LicenseResult result = validateLicense(key, email, liveApiKey()); + LicenseResult result = validateLicense(key, email); ASSERT_TRUE(result.ok()) << "status=" << result.status << " error=" << result.error_message; EXPECT_EQ(result.info.key, key); @@ -245,7 +240,7 @@ TEST(ApiClientLiveTest, ValidateLicenseSuccessLive) { // ─── Live: POST /automations/create/account error paths (harmless) ───── TEST(ApiClientLiveTest, CreateAccountMissingEmailLive) { - AccountResult result = createAccount("", "", liveApiKey()); + AccountResult result = createAccount("", ""); ASSERT_FALSE(result.ok()); EXPECT_EQ(result.status, 400); EXPECT_EQ(result.error_message, "email is required"); @@ -260,7 +255,7 @@ TEST(ApiClientLiveTest, CreateAccountSuccessLive) { GTEST_SKIP() << "RW_LIVE_ACCOUNT_EMAIL not set"; } - AccountResult result = createAccount(email, "RadiantWave Test", liveApiKey()); + AccountResult result = createAccount(email, "RadiantWave Test"); ASSERT_TRUE(result.ok()) << "status=" << result.status << " error=" << result.error_message; EXPECT_TRUE(result.body.success); diff --git a/tests/test_color_palettes.cpp b/tests/test_color_palettes.cpp index dcf1e3e..a49dd48 100644 --- a/tests/test_color_palettes.cpp +++ b/tests/test_color_palettes.cpp @@ -175,7 +175,7 @@ TEST(ColorPalettesLiveTest, FetchColorPalettesLive) { } std::vector palettes; - ASSERT_TRUE(fetchColorPalettes(palettes, env("RW_LIVE_API_KEY"))); + ASSERT_TRUE(fetchColorPalettes(palettes)); EXPECT_FALSE(palettes.empty()); for (const auto& p : palettes) { EXPECT_GE(p.colors.size(), 1u); diff --git a/tests/test_license_service.cpp b/tests/test_license_service.cpp index 9bd9460..d6188b9 100644 --- a/tests/test_license_service.cpp +++ b/tests/test_license_service.cpp @@ -48,7 +48,7 @@ TEST(LicenseServiceTest, NotConfiguredWhenNoEmail) { tmp.db.setKV(KV_LICENSE_KEY, "ABCD-EFGH-IJKL-MNOP"); std::string errorMessage = "stale"; - EXPECT_EQ(validateStoredLicense(tmp.db, "", &errorMessage), + EXPECT_EQ(validateStoredLicense(tmp.db, &errorMessage), Result::NotConfigured); EXPECT_EQ(errorMessage, ""); } @@ -57,7 +57,7 @@ TEST(LicenseServiceTest, NotConfiguredWhenNoLicense) { TempDb tmp; tmp.db.setKV(KV_EMAIL_ADDRESS, "user@example.com"); - EXPECT_EQ(validateStoredLicense(tmp.db, "", nullptr), + EXPECT_EQ(validateStoredLicense(tmp.db, nullptr), Result::NotConfigured); } @@ -117,7 +117,7 @@ TEST(LicenseServiceLiveTest, ValidatesAndGrantsSubscribedState) { tmp.db.setKV(KV_EMAIL_ADDRESS, email); std::string errorMessage; - const Result result = validateStoredLicense(tmp.db, "", &errorMessage); + const Result result = validateStoredLicense(tmp.db, &errorMessage); EXPECT_EQ(result, Result::Ok) << "error=" << errorMessage; // A successful validation is the "subscribed" flag; no subscription-level @@ -134,7 +134,7 @@ TEST(LicenseServiceLiveTest, InvalidPairReportsInvalid) { tmp.db.setKV(KV_EMAIL_ADDRESS, "nobody@example.com"); std::string errorMessage; - EXPECT_EQ(validateStoredLicense(tmp.db, "", &errorMessage), + EXPECT_EQ(validateStoredLicense(tmp.db, &errorMessage), Result::Invalid); EXPECT_FALSE(errorMessage.empty()); }