mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-08 19:56:53 +00:00
fix(web-app-socialhome): rename the household to the inventory title and back
SH_INSTANCE_NAME names the household on the first boot only, and GET /api/instance/config prefers the stored identity row, so a design title set later never reached the household. Upstream renames that row through PATCH /api/admin/instance (admin only, re-broadcast to paired households); the earlier TODO claimed no such route exists, which was wrong. The design tasks now read the current name and converge it with the same semantics as Decidim: household-name.json holds the name the role applied and the one before it. The role renames while the current name is its own, restores the previous name once the title is gone, and leaves a name an administrator chose in the app alone. A fresh install already carries the title from its first boot, so the role records it without a rename; otherwise a later administrator rename would be overwritten on the next deploy. The token, theme and rename tasks moved into tasks/02_design/converge.yml behind one include, because a block may hold at most three tasks. The design spec now checks the instance name and the feed title; the title assertion alone passed with a stale household name, because the design script only replaces the brand suffix. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
bb4646cfc0
commit
92c1f78771
6 files changed
+111
-42
No files matched your search
@@ -40,7 +40,6 @@ Open items that only a live run can settle:
|
||||
|
||||
## Open from the design pass
|
||||
|
||||
- Title override: `SH_INSTANCE_NAME` reaches the home name only on the first boot, because `ensure_instance_identity` (upstream `socialhome/identity_bootstrap.py`) writes `display_name` once and `GET /api/instance/config` prefers that row; an inventory title set later changes the env but not the shown name. No route writes that row at 2026.10.8 (measured: `routes/instance.py` only reads `federation_repo.get_local_identity()`, and no `set`/`update` of the local identity exists in the package), and the row is the identity peers receive. Options: ask upstream for an admin route that renames the household, or accept first-boot only.
|
||||
- Shared sign-in budget: `POST /api/auth/token` allows 5 attempts per 15 minutes per `request.remote`, and the app ignores `X-Forwarded-For`, so every browser behind the front proxy and the deploy's own token request share one bucket (measured: all logins in `/api/admin/auth-audit` come from `192.168.75.1`). At 2026.10.11 the budget is the module constant `AUTH_TOKEN_RATE_LIMIT` in `socialhome/routes/users.py` with no env or TOML key, and `trusted_proxies` exists for the GFS only (`socialhome/global_server/config.py`); the design spec keeps one administrator token in the Playwright staging volume. Options: let the proxy reach the app with the client address (PROXY protocol or upstream trusted-proxy support), or ask upstream to honour a trusted forwarded header.
|
||||
- `tasks/01_setup.yml` comment is stale: at 2026.10.8 the headless seed marks setup complete itself (upstream `platform/standalone/adapter.py`), so the task always answers 409 (measured: `ok`, never `changed`). Options: drop the task or rewrite the comment.
|
||||
- `meta/csp.yml` keeps `script-src-elem unsafe-inline` for an inline theme script the 2026.10.8 shell no longer has (it loads `assets/theme-boot.js`; measured: the served `/feed` carries no inline script of its own). Options: drop the flag after checking `hlth-csp`; the injected design script then needs its CSP hash, which `sys-front-inj-design` adds.
|
||||
|
||||
@@ -569,6 +569,12 @@ exports.register = function () {
|
||||
}
|
||||
await expect(page.locator("link[rel~='icon']").first()).toHaveAttribute("href", faviconUrl);
|
||||
}
|
||||
if (title) {
|
||||
const config = await apiGetOnion(page.request, `${base}/api/instance/config`);
|
||||
expect((await config.json()).instance_name, "the household carries the configured title").toBe(title);
|
||||
await visit(page, `${base}/feed`);
|
||||
await expect(page.locator(TOPBAR_TITLE), "the feed is named after the household").toHaveText(title);
|
||||
}
|
||||
});
|
||||
|
||||
test("design: the sign-in page shows the generated logo", async ({ page }) => {
|
||||
|
||||
@@ -5,48 +5,20 @@
|
||||
checksum_algorithm: sha1
|
||||
register: socialhome_theme_marker
|
||||
|
||||
- name: "🎨 Converge the household primary colour"
|
||||
when: SOCIALHOME_DESIGN_ENABLED | bool or socialhome_theme_marker.stat.exists
|
||||
block:
|
||||
- name: "🔑 Request an administrator token for the household theme"
|
||||
uri_retry:
|
||||
url: "{{ SOCIALHOME_INTERNAL_URL }}/api/auth/token"
|
||||
method: POST
|
||||
body_format: json
|
||||
body:
|
||||
username: "{{ SOCIALHOME_ADMIN_USERNAME }}"
|
||||
password: "{{ SOCIALHOME_ADMIN_PASSWORD }}"
|
||||
status_code: 200
|
||||
register: socialhome_theme_token
|
||||
no_log: "{{ MASK_CREDENTIALS_IN_LOGS | bool }}"
|
||||
- name: "📥 Look up the household name the role wrote last"
|
||||
ansible.builtin.stat:
|
||||
path: "{{ SOCIALHOME_NAME_STATE_FILE }}"
|
||||
register: socialhome_name_marker
|
||||
|
||||
- name: "🎨 Read the household theme"
|
||||
uri_retry:
|
||||
url: "{{ SOCIALHOME_INTERNAL_URL }}/api/theme"
|
||||
method: GET
|
||||
headers:
|
||||
Authorization: "Bearer {{ socialhome_theme_token.json.token }}"
|
||||
status_code: 200
|
||||
register: socialhome_theme
|
||||
no_log: "{{ MASK_CREDENTIALS_IN_LOGS | bool }}"
|
||||
- name: "📥 Read the household name the role wrote last"
|
||||
ansible.builtin.slurp:
|
||||
src: "{{ SOCIALHOME_NAME_STATE_FILE }}"
|
||||
register: socialhome_name_state_file
|
||||
when: socialhome_name_marker.stat.exists
|
||||
|
||||
- name: "🎨 Write the household primary colour"
|
||||
uri_retry:
|
||||
url: "{{ SOCIALHOME_INTERNAL_URL }}/api/theme"
|
||||
method: PUT
|
||||
body_format: json
|
||||
body:
|
||||
primary_color: "{{ SOCIALHOME_THEME_PRIMARY }}"
|
||||
headers:
|
||||
Authorization: "Bearer {{ socialhome_theme_token.json.token }}"
|
||||
status_code: 200
|
||||
register: socialhome_theme_written
|
||||
changed_when: true
|
||||
when:
|
||||
- socialhome_theme.json.primary_color | lower != SOCIALHOME_THEME_PRIMARY
|
||||
- socialhome_theme.json.primary_color | lower == SOCIALHOME_BRAND_PRIMARY or (socialhome_theme_marker.stat.exists and
|
||||
socialhome_theme_marker.stat.checksum == (socialhome_theme.json.primary_color | lower | hash('sha1')))
|
||||
no_log: "{{ MASK_CREDENTIALS_IN_LOGS | bool }}"
|
||||
- name: "🎨 Converge the household primary colour and name"
|
||||
include_tasks: 02_design/converge.yml
|
||||
when: SOCIALHOME_DESIGN_ENABLED | bool or socialhome_theme_marker.stat.exists or socialhome_name_marker.stat.exists
|
||||
|
||||
- name: "🏷️ Mark the household primary colour as the role's"
|
||||
stack_host_copy:
|
||||
@@ -57,8 +29,24 @@
|
||||
- SOCIALHOME_DESIGN_ENABLED | bool
|
||||
- socialhome_theme_written is changed
|
||||
|
||||
- name: "🏷️ Mark the household name as the role's"
|
||||
stack_host_copy:
|
||||
content: "{{ {'applied': SOCIALHOME_DESIGN_TITLE, 'previous': (socialhome_name_state.previous if socialhome_name_state | length > 0 else socialhome_name_current)}
|
||||
| to_json }}"
|
||||
dest: "{{ SOCIALHOME_NAME_STATE_FILE }}"
|
||||
mode: "0644"
|
||||
when:
|
||||
- SOCIALHOME_DESIGN_TITLE | length > 0
|
||||
- socialhome_name_written is changed or (socialhome_name_state | length == 0 and socialhome_name_current == SOCIALHOME_DESIGN_TITLE)
|
||||
|
||||
- name: "🧹 Drop the household theme marker"
|
||||
ansible.builtin.file:
|
||||
path: "{{ SOCIALHOME_THEME_MARKER_FILE }}"
|
||||
state: absent
|
||||
when: not (SOCIALHOME_DESIGN_ENABLED | bool)
|
||||
|
||||
- name: "🧹 Drop the household name marker"
|
||||
ansible.builtin.file:
|
||||
path: "{{ SOCIALHOME_NAME_STATE_FILE }}"
|
||||
state: absent
|
||||
when: SOCIALHOME_DESIGN_TITLE | length == 0
|
||||
@@ -0,0 +1,73 @@
|
||||
---
|
||||
- name: "🔑 Request an administrator token for the household theme"
|
||||
uri_retry:
|
||||
url: "{{ SOCIALHOME_INTERNAL_URL }}/api/auth/token"
|
||||
method: POST
|
||||
body_format: json
|
||||
body:
|
||||
username: "{{ SOCIALHOME_ADMIN_USERNAME }}"
|
||||
password: "{{ SOCIALHOME_ADMIN_PASSWORD }}"
|
||||
status_code: 200
|
||||
register: socialhome_theme_token
|
||||
no_log: "{{ MASK_CREDENTIALS_IN_LOGS | bool }}"
|
||||
|
||||
- name: "🎨 Read the household theme"
|
||||
uri_retry:
|
||||
url: "{{ SOCIALHOME_INTERNAL_URL }}/api/theme"
|
||||
method: GET
|
||||
headers:
|
||||
Authorization: "Bearer {{ socialhome_theme_token.json.token }}"
|
||||
status_code: 200
|
||||
register: socialhome_theme
|
||||
no_log: "{{ MASK_CREDENTIALS_IN_LOGS | bool }}"
|
||||
|
||||
- name: "🎨 Write the household primary colour"
|
||||
uri_retry:
|
||||
url: "{{ SOCIALHOME_INTERNAL_URL }}/api/theme"
|
||||
method: PUT
|
||||
body_format: json
|
||||
body:
|
||||
primary_color: "{{ SOCIALHOME_THEME_PRIMARY }}"
|
||||
headers:
|
||||
Authorization: "Bearer {{ socialhome_theme_token.json.token }}"
|
||||
status_code: 200
|
||||
register: socialhome_theme_written
|
||||
changed_when: true
|
||||
when:
|
||||
- socialhome_theme.json.primary_color | lower != SOCIALHOME_THEME_PRIMARY
|
||||
- socialhome_theme.json.primary_color | lower == SOCIALHOME_BRAND_PRIMARY or (socialhome_theme_marker.stat.exists and socialhome_theme_marker.stat.checksum
|
||||
== (socialhome_theme.json.primary_color | lower | hash('sha1')))
|
||||
no_log: "{{ MASK_CREDENTIALS_IN_LOGS | bool }}"
|
||||
|
||||
- name: "🏷️ Read the household name"
|
||||
uri_retry:
|
||||
url: "{{ SOCIALHOME_INTERNAL_URL }}/api/instance/config"
|
||||
method: GET
|
||||
status_code: 200
|
||||
register: socialhome_instance_config
|
||||
|
||||
- name: "🏷️ Hold the household name and the name the role wrote last"
|
||||
ansible.builtin.set_fact:
|
||||
socialhome_name_current: "{{ socialhome_instance_config.json.instance_name }}"
|
||||
socialhome_name_state: "{{ (socialhome_name_state_file.content | b64decode | from_json) if socialhome_name_marker.stat.exists else {} }}"
|
||||
|
||||
- name: "🏷️ Rename the household"
|
||||
uri_retry:
|
||||
url: "{{ SOCIALHOME_INTERNAL_URL }}/api/admin/instance"
|
||||
method: PATCH
|
||||
body_format: json
|
||||
body:
|
||||
display_name: "{{ socialhome_name_target }}"
|
||||
headers:
|
||||
Authorization: "Bearer {{ socialhome_theme_token.json.token }}"
|
||||
status_code: 200
|
||||
vars:
|
||||
socialhome_name_ours: "{{ socialhome_name_state | length > 0 and socialhome_name_current == socialhome_name_state.applied }}"
|
||||
socialhome_name_target: >-
|
||||
{{ (socialhome_name_state.previous if socialhome_name_ours | bool else '') if SOCIALHOME_DESIGN_TITLE | length == 0
|
||||
else ('' if (socialhome_name_state | length > 0 and not (socialhome_name_ours | bool)) or socialhome_name_current == SOCIALHOME_DESIGN_TITLE
|
||||
else SOCIALHOME_DESIGN_TITLE) }}
|
||||
register: socialhome_name_written
|
||||
changed_when: true
|
||||
when: socialhome_name_target | length > 0
|
||||
no_log: "{{ MASK_CREDENTIALS_IN_LOGS | bool }}"
|
||||
@@ -22,5 +22,5 @@
|
||||
- name: "🔐 Complete the standalone first-boot setup for '{{ application_id }}'"
|
||||
include_tasks: 01_setup.yml
|
||||
|
||||
- name: "🎨 Converge the household theme for '{{ application_id }}'"
|
||||
- name: "🎨 Converge the household theme and name for '{{ application_id }}'"
|
||||
include_tasks: 02_design.yml
|
||||
@@ -16,6 +16,9 @@ SOCIALHOME_CONFIG_HOST_FILE: "{{ [lookup('container', application_id, 'directori
|
||||
SOCIALHOME_MAX_STORAGE_BYTES: 4294967296
|
||||
|
||||
SOCIALHOME_DESIGN_ENABLED: "{{ lookup('config', application_id, 'services.design.enabled') | bool }}"
|
||||
SOCIALHOME_DESIGN_TITLE: "{{ lookup('design', application_id).title if SOCIALHOME_DESIGN_ENABLED | bool and lookup('design', application_id).title is string else
|
||||
'' }}"
|
||||
SOCIALHOME_NAME_STATE_FILE: "{{ [lookup('container', application_id, 'directories.volumes'), 'household-name.json'] | path_join }}"
|
||||
SOCIALHOME_BRAND_PRIMARY: "#d2542a"
|
||||
SOCIALHOME_THEME_PRIMARY: >-
|
||||
{{ (lookup('design_palette', lookup('config', 'web-svc-design', 'services.design.colors.base')).light['--design-primary'] | lower)
|
||||
|
||||
Reference in new issue
Block a user