Repository navigation
Vue console 2.2.0: Legacy parity, build-log search, Cypress gate, Maps removed - #33
Merged
Merged
Conversation
…href Closes the phase 22 CI-03 gap found in 22-VERIFICATION. The authenticated Layout footer bound this.$hostnames.CONSOLE and .LANDING without declaring the hostnames mixin, so it read the empty Vue.prototype.$hostnames from main.js and both "THiNX Console" and "THiNX Cloud" rendered with no href. Layout now imports and declares the mixin, the same per-component pattern Login, PasswordReset, OAuthReturn and Visits already use. The template is unchanged. Adds tests/unit/footer-hostnames.cjs, a plain-node test chained into test:unit. It renders the real footers of Layout, Login and PasswordReset with each component's own mixins, checks the unset-var fallback to the serving origin, and fails if any $hostnames reader under src lacks the mixin. Claude-Session: https://claude.ai/code/session_01JdkLTubatsxPk7D9BTb4gS
- $.ajaxSetup beforeSend in app/js/thinx-api.js echoes the XSRF-TOKEN cookie, read at send time, as X-XSRF-TOKEN on urlBase and same-origin relative calls - never sent to a foreign or protocol-relative origin; contentType and withCredentials defaults unchanged - test/xsrf-seam.cjs (npm run test:xsrf) proves it on plain node
Aikido flags the image for running as root. The runtime stage now ends in USER nginx (uid 101); nginx keeps port 80 via the NET_BIND_SERVICE file capability, drops the user directive, writes its pid to /tmp and owns /var/cache/nginx.
GET /api/oauth/{google,github} destroys the API session before the
provider redirect, but the browser keeps XSRF-TOKEN. The return page
trusted that cookie, so POST /login and POST /session/token reached the
API with a token bound to a destroyed session (session_mismatch in
observe, a 403 + retry under CSRF_MODE=signed). Force the shared prime
on /oauth-return in both App.vue's hydrate and OAuthReturn.vue.
XPCDP none, Referrer-Policy and the D-14 Permissions-Policy (all always), the canonical CSP with each image's placeholder, and proxy_hide_header Content-Security-Policy on every proxy location (D-19). The Vue image keeps 'unsafe-eval' out of script-src: tests/security/csp.cjs forbids it, and the parity check allows exactly that difference.
- tests/unit/log-paging-store.cjs drives History.vue -> auditlog store -> Api.parseResult -> a fake network in plain node - RED: parseResult drops paging, the store requests /logs/audit without limit, fetchAuditPage and loadMoreAudit do not exist yet - wired into npm run test:unit Claude-Session: https://claude.ai/code/session_012iQfzaaG8d2an2Jo67EfbU
- Api.parseResult keeps `paging` next to `response`; legacy shape unchanged (D-19) - store/logPaging.js: PAGE_SIZE, normPaging, pagedPath (URL-encodes the cursor) - auditlog: first page via ?limit=100 with paging state, fetchAuditPage that never mutates state and never throws (D-02, D-04) - History.vue: audit paging footer outside the empty/table pair; Load more appends to History's own rows and disappears on the last page (D-01) Claude-Session: https://claude.ai/code/session_012iQfzaaG8d2an2Jo67EfbU
…ase 26)
- buildlog first page ?limit=100 with paging; fetchBuildPage normalizes the
nested shape and the flat {date, udid} shape
- History build Load more, tables page independently (D-03), and a
background first-page refresh leaves History's paged rows alone
Claude-Session: https://claude.ai/code/session_012iQfzaaG8d2an2Jo67EfbU
- buildlog: first page via ?limit=100 with paging state and fetchBuildPage,
every page through normalizeBuildItems; Header, Notifications,
DeviceDetail and the dashboard get the newest 100 builds (D-04)
- normalizeBuildItems reads udid/date from the flat {date, udid} item
- History.vue: build paging footer and loadMoreBuilds; each table pages
with its own cursor and state (D-03)
Claude-Session: https://claude.ai/code/session_012iQfzaaG8d2an2Jo67EfbU
- filter hint singular/plural with toLocaleString, no-match line keeps Load more, load error keeps rows and retries, colour rules - initial load must clear Loading... on a rejected first page - busy state (aria-disabled, aria-busy, one request) and live region copy Claude-Session: https://claude.ai/code/session_012iQfzaaG8d2an2Jo67EfbU
- filter hint next to Load more when older entries exist and a filter is active, singular/plural with toLocaleString (D-05); no requests on filter change (D-06) - "No loaded entries match these filters." keeps Load more reachable; the existing empty-state lines show only when nothing is loaded - failed Load more keeps rows and cursor, shows a role=alert line, and the button retries; red only on the icon, body colour text - aria-disabled (never disabled) and aria-busy while loading, polite live region after appends, focus moves to the footer after the last page - initial load uses allSettled + finally so Loading... always clears - scoped .log-paging styles per the UI-SPEC Claude-Session: https://claude.ai/code/session_012iQfzaaG8d2an2Jo67EfbU
…se 26) - visitApp stubs POST /api/v2/session/token with the forged token before cy.visit, so hydrateSession's memory-only token exchange succeeds and a stubbed session lands in the app instead of /login - visitAppRoute enters through the dashboard and navigates in-app by hash, working around the pre-existing App.vue deep-link redirect after hydrate - device-detail.spec.js enters /app/devices and /app/device/udid-z through it Claude-Session: https://claude.ai/code/session_012iQfzaaG8d2an2Jo67EfbU
…hase 26) - four synthetic two-page fixtures (audit and nested build shape) with paging.has_more / next_cursor - history.spec.js 'paging (LOG-03/LOG-04)' covers UI-SPEC assertions 1-7: legacy fixtures show no Load more; Load more sends limit=100 with the page-1 cursor and appends; tables page independently; the filter hint shows only with has_more and an active filter and filtering sends no request; a no-match filter keeps Load more; a 500 keeps rows and the button and a retry appends; dashboard cards stay on the first page - HIST-01..05 enter History through cy.visitAppRoute Claude-Session: https://claude.ai/code/session_012iQfzaaG8d2an2Jo67EfbU
- tests/unit/table-row-contrast.cjs compiles the real theme with sass and runs a mini-cascade for History's warning and danger audit rows - WCAG AA 4.5:1 on even and odd rows over the #272B4E panel, dark-tint check, plus info-row and flag-badge guards - wired into npm run test:unit; fails on the current theme (G-26-1) Claude-Session: https://claude.ai/code/session_012iQfzaaG8d2an2Jo67EfbU
…e 26) - tr.table-warning / tr.table-danger: transparent row, cells tinted at .2 alpha of the theme colour, $header-color text, theme border colour - text contrast 7.71/7.91 (warning) and 9.12/9.35 (danger) even/odd over the #272B4E panel; $text-color would give 4.44:1 and fail AA - info rows and flag badges unchanged (G-26-1) Claude-Session: https://claude.ai/code/session_012iQfzaaG8d2an2Jo67EfbU
- src/test/toast-escape.cjs loads LogviewController.js in a vm with fake angular/jQuery/toastr/WebSocket and feeds frames through wss.onmessage - asserts body/title/JSON body are escaped, msg.type is allowlisted to info/success/warning/error, and invalid nids drop the actionable toast - legitimate frames keep their markup and Yes/No/Send wiring - npm run test:toast Claude-Session: https://claude.ai/code/session_012iQfzaaG8d2an2Jo67EfbU
Actionable and plain notification frames reach the device owner's
console (quick 261003-vn3), and a transferred device's previous owner can
still publish them. parseNotification concatenated msg.body, msg.title,
msg.nid and JSON.stringify(msg.body) into toastr HTML and indexed toastr
with msg.type.
- thinxEscapeHtml escapes & < > " ' and backtick for body, title and the
JSON body; rendered text is unchanged for legitimate frames
- thinxSafeNid accepts only /^[A-Za-z0-9_-]{1,128}$/ before nid is used in
ids, names or jQuery selectors; otherwise the actionable frame is dropped
- thinxToastMethod allowlists info/success/warning/error; any other
msg.type returns without toasting
- Yes/No/Send wiring and submitNotificationResponse emits unchanged; the
websocket frame contract is untouched
Claude-Session: https://claude.ai/code/session_012iQfzaaG8d2an2Jo67EfbU
POST /api/v2/apikey answers {api_key, hash}; Apikeys.vue reads .key, so
the copy-your-key dialog never opens.
Claude-Session: https://claude.ai/code/session_012iQfzaaG8d2an2Jo67EfbU
The list API no longer returns key (quick 261003-w13). Claude-Session: https://claude.ai/code/session_012iQfzaaG8d2an2Jo67EfbU
Implement v1.16 console usability, build logs, transformers and deploy keys
- Render the real Linked Repository card from DeviceDetail.vue with its computed - Assert https links open in a new tab with noopener noreferrer - Assert javascript: and git@ URLs stay plain text with no href
- Add linkedRepositoryHref computed: WHATWG URL parse, http:/https: only, returns parsed href - Linked Repository card renders an anchor with target=_blank rel=noopener noreferrer - SSH remotes, javascript:, data: and unparseable values stay plain text
- Scheme matrix over linkedRepositoryHref: case and whitespace variants, javascript:, data:, ssh:, file:, git@, bare host, empty, undefined, null - Render cases for an unmatched device source and an ssh:// URL
The editor stayed saveable after a failed load, so Save wrote an empty alias and body over the live transformer. The editor now tracks a loaded flag and disables Save until load succeeds and the alias is non-empty. updateItem refuses an empty alias or missing body, and reports a transformer deleted since load instead of silently writing.
The build row was role="button" with Enter/Space handlers, so keydown on the inner Expand button bubbled up, was prevented and opened the log dialog. The row drops role, tabindex and key handlers. Row click stays as a mouse shortcut that ignores inner controls and text selection; keyboard users use the per-row "Open build log" button, which now carries the build id label.
Create & Assign built the assignment from the multi-select's live v-model, so an unsaved selection change was written to the device as a side effect. It now assigns from device.transformers plus the new utid, and keeps the user's unsaved selection pending in the select after the reload.
The Actions-card Environment button stayed enabled while the editor was open and re-fetched the device, overwriting the unsaved draft. It now opens the editor only when closed, never reloads an open draft, and scrolls to and focuses the draft textarea. editEnvironment also refuses to run while editing.
… (48-REVIEW WR-01)
A body stream that fails after the headers arrived (connection reset, page unload, an
aborted Cypress intercept) rejected out of request() because response.text() sat outside
the try. It now resolves { success: false, status, response: null, networkError: true }
like a request that never got a response. close-out-v118 I9 covers the failed body read.
Claude-Session: https://claude.ai/code/session_014ZVXKS2uhAMrLdLzKmNufX
… WR-03) test_vue runs yarn with no lockfile, so unpinned lifecycle scripts run with the job's environment, and no step uses Sonar. The Cypress suites stub the API and need no credential; the parent repo's test-vue-console already runs them with no context (D-01). Claude-Session: https://claude.ai/code/session_014ZVXKS2uhAMrLdLzKmNufX
…mpty toast (48-REVIEW WR-04)
The build reply is { success, response } with no status field, so the failed-build toast
read response.status and showed a titled toast with an undefined body. It now shows one fixed
sentence and no reply value reaches toastr (it renders HTML). legacy-close-out G9 covers it.
Claude-Session: https://claude.ai/code/session_014ZVXKS2uhAMrLdLzKmNufX
…debug logs The Build and Publish Vue console job uses no SonarCloud variable, so the context only widened what that job could read. Also remove the "+++ updateSources" and "+++ updateApikeys" console.log lines from the Legacy Devices, Device and Source controllers (UAT 48 test 9 observation). Claude-Session: https://claude.ai/code/session_014ZVXKS2uhAMrLdLzKmNufX
… controllers Deploykey, Device and Channel controllers still logged "+++ updateDeploykeys", "+++ updateChannels" and "+++ updateLatestFirmwareEnvelope" to the browser console on every list refresh. No source line under src/app logs "+++" now. Claude-Session: https://claude.ai/code/session_014ZVXKS2uhAMrLdLzKmNufX
- audit-gaps-v118.cjs: guard harness evaluating src/Routes.js with stubs, checks G1..G6 - deep-link.spec.js: admin hard load of /app/admin/users on profile-admin.json (additions only) - api-stubs.js: default GET /api/v2/admin/users stub aliased getAdminUsers, admin-users.json - package.json: test:unit chains the new file; fails on the current tree (G1, G2, G3, Cypress) Claude-Session: https://claude.ai/code/session_014ZVXKS2uhAMrLdLzKmNufX
- Routes.js: isAdminSession() fetches the profile only while the store holds the initial empty object, decides from admin === true, never rejects, fails closed to the dashboard - deep-link.spec.js: the logged-out test settles its two session-token posts before it ends, so the admin test that follows inherits no in-flight request (48-08 lesson, addition only) - unit G1..G6 and the admin deep-link browser test are green Claude-Session: https://claude.ai/code/session_014ZVXKS2uhAMrLdLzKmNufX
…OTIF-04, CI-07)
- deep-link.spec.js: admin reload stays on the user list; a non-admin session lands on the
dashboard without requesting the user list; a logged-out load lands on /login with no
profile read; every test settles its stubbed reads by alias (additions only, 8 tests)
- audit-gaps-v118.cjs: Y1 spec hygiene via specHygiene(file, { tests }) with the forbidden
markers as data (skip, only, xit, retries, numeric wait, live login, Cypress.env, URLs)
- three consecutive runs of the spec are green, test:unit 442 ok
Claude-Session: https://claude.ai/code/session_014ZVXKS2uhAMrLdLzKmNufX
Extend tests/unit/audit-gaps-v118.cjs with the loaders copied from close-out-v118.cjs, a storeRun helper (real Api over a replaced fetch) and table-driven checks S1..S3 and P1..P4, registered for apikeys only. On the current tree S1, S3, P1, P2, P3 and P4 fail: apikeys/fetchItems resolves on a network failure, createItem rejects when only the follow-up read fails, Apikeys.vue loadData leaves an unhandled rejection, has no loadFailed flag or load-error alert, and a stored delete keeps the old rows. Claude-Session: https://claude.ai/code/session_014ZVXKS2uhAMrLdLzKmNufX
node tests/unit/audit-gaps-v118.cjs --tap prints the same results as TAP version 13 (ok N - name, not ok N - name, detail as a comment, trailing plan), the report format the TDD red-evidence classifier reads. The default ok/FAIL line output that test:unit and the plan verifies grep is unchanged. Claude-Session: https://claude.ai/code/session_014ZVXKS2uhAMrLdLzKmNufX
…it W2) store/apikeys.js: fetchItems throws Could not reach the server. when the request never got a response (result.networkError) and leaves the list as it was; a refused or empty reply resolves as before. createItem and deleteItems go through a file-local refreshAfter, so a stored write whose follow-up read fails resolves with refreshed: false instead of rejecting. Apikeys.vue: loadData is async with try/catch/finally; a failed load clears the rows, the selection and the action error and shows one danger alert (data-cy apikeys-load-error) in place of the List. deleteSelected re-syncs the rows from the store after a stored delete and reloads on refreshed: false. No import line changed. Claude-Session: https://claude.ai/code/session_014ZVXKS2uhAMrLdLzKmNufX
…(audit W2) Widen the S and P table-driven checks of audit-gaps-v118.cjs to channels and repositories, add S1b (repositories/fetchItems rejects through fetchRepositories), P3b (the PlatformBullet cell slot stays) and P3c (the create-dialog alert is not the load error); the page danger-alert count now ignores alerts inside a b-modal. On the current tree S1, S1b, S3, P1, P2, P3, P3c and P4 fail on the channels and repositories rows for the same reasons the apikeys row failed before. Claude-Session: https://claude.ai/code/session_014ZVXKS2uhAMrLdLzKmNufX
…hed (audit W2) store/channels.js and store/repositories.js: the fetch action (fetchItems, fetchRepositories; repositories/fetchItems still delegates) throws Could not reach the server. when the request never got a response (result.networkError) and keeps the list; a refused or empty reply resolves as before. createItem and deleteItems go through a file-local refreshAfter (fetchItems, respectively fetchRepositories), so a stored write with a failed follow-up read resolves with refreshed: false. Channels.vue and Repositories.vue: async loadData with try/catch/finally; a failed load clears rows, selection and action error and shows one danger alert (channels-load-error, repositories-load-error) in place of the List; deleteSelected re-syncs from the store or reloads on refreshed: false. The PlatformBullet cell slot, the create flow and the dialog alert are unchanged. No import line changed. Claude-Session: https://claude.ai/code/session_014ZVXKS2uhAMrLdLzKmNufX
Widen S1..S3 of audit-gaps-v118.cjs to the profile store (fetchProfile, updateProfile, uploadAvatar) and add P5 (Profile page load error, form reset, saves reloading on refreshed: false), K1 (Header mounted), K2 (Login login), K3 (OAuthReturn complete) and K4 (DeviceDetail loadDevice through the real repositories store). On the current tree S1 and S3 fail for profile, P5 fails because loadProfile rejects, K1 records an unhandled rejection from the Header, and K2 and K3 reject out of sign-in when the profile read fails; S2 and K4 already hold. Claude-Session: https://claude.ai/code/session_014ZVXKS2uhAMrLdLzKmNufX
… W2) store/profile.js: fetchProfile throws Could not reach the server. when the request never got a response (result.networkError) and keeps the profile; a refused or empty reply resolves as before. updateProfile and uploadAvatar go through a file-local refreshAfter, so a stored save whose follow-up read fails resolves with refreshed: false. Profile.vue: loadProfile is wrapped in try/catch/finally; a failed read clears the profile, the form and the notification flags and shows one danger alert (data-cy profile-load-error) in place of the tabs. saveProfile and saveAvatar reload on refreshed: false and keep the store copy otherwise. Header.vue appends a catch to the mounted fetchProfile chain (the unhandled rejection of 48-08); Login.vue and OAuthReturn.vue wrap only the fetchProfile await, so a failed profile read never blocks sign-in and the user still reaches /app/dashboard. No import line changed. Claude-Session: https://claude.ai/code/session_014ZVXKS2uhAMrLdLzKmNufX
…-07, audit W3)
- rsakeys.spec.js: rename sends PATCH /rsakey { filename, name }, delete sends
DELETE /rsakey { filenames }, a declined delete sends nothing; stubs only,
every test asserts the catch-all stayed empty
- api-stubs.js: default getRsakeys, renameRsakey, deleteRsakeys and the map entries
- fixtures rsakeys*.json hold fictional keys only
- audit-gaps-v118.cjs: specHygiene unstubbed option, fixtureHygiene, rows Y1..Y3
Claude-Session: https://claude.ai/code/session_014ZVXKS2uhAMrLdLzKmNufX
…ing suite (CI-07, audit W3)
- transformers.spec.js: the typed-confirm bulk delete names both transformers and the
device usage, stays disabled for any other word, writes one POST /profile holding
only the kept transformer (stateful read-before-write stub), and a dismissed dialog
sends nothing
- enviros.spec.js: one PUT /env { key, value } that never shows the value; a failed
write keeps the dialog, the typed value and the key's current value
- api-stubs.js: default putEnv and the map entries for the two profile fixtures
- audit-gaps-v118.cjs: Y1 rows for both specs, Y2 alias putEnv, Y3 fixtures
Claude-Session: https://claude.ai/code/session_014ZVXKS2uhAMrLdLzKmNufX
…gating suite (CI-07, audit W3) - repositories.spec.js: the shared platform bullet (Arduino, Platform.io, MongooseOS, unknown; no raw platform string, no Platform: label) and the W2 network failure (one repositories-load-error alert, no table, Delete (0) disabled) - build-log-search.spec.js: case-insensitive highlight and count (1 / 3), next and previous with wrap, the three-character threshold, No matches disabling the step buttons, and a reopened dialog forgetting the search - not asserted: the log box scrolling to the stepped match; BuildLogDialog measures the modal body one tick before the b-modal transporter re-renders it, so scrollTop stays 0 (pre-existing, recorded in the phase deferred items, vue/src untouched here) - api-stubs.js: opt-in cy.stubSources() (GET /source) and the two fixture map entries - audit-gaps-v118.cjs: Y1 rows for both specs, Y2 command stubSources, Y3 fixtures Claude-Session: https://claude.ai/code/session_014ZVXKS2uhAMrLdLzKmNufX
…48.1-REVIEW WR-01)
auth/clearSession never reset profile/profile, so after an in-tab sign-out the store kept
the signed-out account. The admin guard's shortcut (a non-empty profile is decided without a
request) and the sign-in pages' swallowed fetchProfile failure then made that stale object
authoritative: a second account inherited the first one's admin: true in the Sidebar and the
admin guard, and auth/setUser was committed with the other identity. The API still enforced
admin on every /api/v2/admin route; this is a UX-gate and identity-display defect.
- auth/logout commits profile/saveProfile {} at the root after clearSession. Not inside
clearSession: scheduleExpiry.expire calls it and then silently re-hydrates the same account,
and the Header does not refetch, so wiping there would blank the admin menu.
- Login.login() and OAuthReturn.complete() commit profile/saveProfile {} before fetchProfile,
so a failed read leaves {} rather than the previous account.
- Routes.js documents what the shortcut relies on.
- audit-gaps-v118.cjs: G7 pins logout resets / clearSession keeps / the guard fetches the next
account; G8 pins both sign-in pages reset before the read and hand {} to setUser; K2/K3 expect
the extra commit. close-out-v118.cjs H2 provides commit and asserts the root reset.
Claude-Session: https://claude.ai/code/session_014ZVXKS2uhAMrLdLzKmNufX
…-REVIEW WR-01)
Starting an impersonation swapped the token under the still-mounted Layout and pushed the
dashboard without touching profile/profile, so the admin's own profile (admin: true) stayed
authoritative for the impersonated session: the Sidebar kept the ADMIN menu, the Routes.js
admin guard let /app/admin/users through on its "non-empty store" shortcut (the page then
failed on the rejected token) and the Header showed the admin's name. Exiting ran clearSession
and pushed /login, where Login.created re-hydrates the admin's cookie session without login(),
so the reset added for the iteration-1 WR-01 never ran there either.
AdminUsers.confirmImpersonate now commits profile/saveProfile {} after persistSession and
reads the impersonated user's profile with the new token, absorbing a rejection as Login does
(a failed read leaves {} and the guard and Sidebar fail closed until the Header retries).
ImpersonationBanner.exit commits the same reset after clearSession, before /login. The
Routes.js isAdminSession comment names all four in-tab identity switches that keep the
shortcut's invariant.
audit-gaps-v118.cjs G9 drives the real confirmImpersonate (resolving and rejecting read) and
exit() with imports stubbed: token swap, reset, read, navigate in that order; one reset commit
each; the guard harness seeded with the result redirects /app/admin/users to the dashboard
without a request (impersonated profile) or fetches and fails closed ({}).
Claude-Session: https://claude.ai/code/session_014ZVXKS2uhAMrLdLzKmNufX
…REVIEW WR-01) Header.vue rendered the name and avatar from a local data.profile copied once in mounted(). Layout.vue keeps the Header mounted across every /app navigation, so after an admin started an impersonation (AdminUsers.confirmImpersonate updates the store) the top bar kept showing the admin for the whole impersonated session while the banner beneath it named the target; the claim in 47fffe7 that Header and banner agree did not hold. A name saved on the Profile page was likewise not reflected until a remount. profile is now a computed over the profile/getProfile getter (falling back to an empty object), the data field is gone and mounted() keeps its single fetchProfile() with the catch that absorbs a rejected read; the deep-link spec's per-load profile read count is unchanged. Tests: K1 asserts profile is a computed (no data field), one read per mount, the rejected and working reads through a store stand-in, and the undefined-getter fallback. G9 gains a rendered Header over one reactive store stand-in shared with the real confirmImpersonate() and exit(): the rendered name goes admin, impersonated user, blank, with a single Header read and no remount. close-out I8 and M1 render the Header through a getProfile stub instead of assigning the former data field (assigning a computed would warn and fail M1). Both new assertions fail against the previous Header. Claude-Session: https://claude.ai/code/session_014ZVXKS2uhAMrLdLzKmNufX
…ervice_unavailable - Rsakeys.vue: KEY_SERVICE_UNAVAILABLE constant; create() shows it for that code only - invalid_key_name and the generic generate error are unchanged - deploy-key-actions G2: the sentence is shown, the code never rendered, the name kept Claude-Session: https://claude.ai/code/session_01AkccHxz4x7iue88b1jw7Ma
…itHub-link result - github-token-scopes: key_service_unavailable maps to the D-03 sentence byte for byte - the existing GitHub-link codes keep their sentences Claude-Session: https://claude.ai/code/session_01AkccHxz4x7iue88b1jw7Ma
…e Profile GitHub link - Profile.vue: the GitHub-link messages map gains key_service_unavailable - the sentence matches the Deploy Keys page; the other codes are unchanged Claude-Session: https://claude.ai/code/session_01AkccHxz4x7iue88b1jw7Ma
…vailable - D11: the D-03 sentence from .done and from a 503 .fail; other 503s stay generic - the code joins FORBIDDEN and the sentence joins ALLOWED for the D9 sweep Claude-Session: https://claude.ai/code/session_01AkccHxz4x7iue88b1jw7Ma
…e legacy console - DeploykeyController: KEY_SERVICE_UNAVAILABLE beside CREATE_FAILED - used in the .done non-success branch and in .fail from responseJSON (the 503 path) - every other path keeps CREATE_FAILED or the key-name sentence Claude-Session: https://claude.ai/code/session_01AkccHxz4x7iue88b1jw7Ma
The demo map left the dashboard and no page renders a Maps component, so vue2-google-maps, its registration in main.js and the GOOGLE_MAPS_APIKEY / VUE_APP_GOOGLE_MAPS_APIKEY build args are dead. The key was also baked into the client bundle. A future device map should be designed fresh, from reported locations or IP-inferred ones, and add its CSP origin on purpose. The legacy console keeps GOOGLE_MAPS_APIKEY for its static map image. Claude-Session: https://claude.ai/code/session_01AkccHxz4x7iue88b1jw7Ma
Up to standards ✅🟢 Issues
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Brings
mainup tothinx-staging(193 commits, fast-forward, nothing onmainis missing from the branch). This is the console half of the parent repo's 1.19.0 release (suculent/thinx-device-api#575), which pointsservices/consoleat3a372a5.Vue console (2.1.5 → 2.2.0)
confirmbulk delete, atomic variable edit), unsaved-change guards on Device Detail.hasBuildLogpredicate and one status-colour scheme on every build list; WCAG 2.5.3 View label.test-vue-consoleCI job (unit tests, build, 16 Cypress specs) now gates the Vue image builds.vue2-google-mapsplugin, its dependency and theGOOGLE_MAPS_APIKEY/VUE_APP_GOOGLE_MAPS_APIKEYbuild args fromvue/Dockerfile. The dashboard demo map is gone, and a future device map is a planned feature.Legacy console
key_service_unavailable. It keeps its Google static-map key.Checks
Vue unit tests, the production build and the CSP dist check pass locally. The parent repo's CircleCI pipeline was green with the console at ec5b7f1 (the Maps removal); the run for the 2.2.0 version bump is in progress.