From 9f1fd7c730717943da0d9588826dd322981e1204 Mon Sep 17 00:00:00 2001 From: zhanghui Date: Sun, 20 Sep 2026 10:53:54 +0800 Subject: [PATCH] revert(*): back out the web settings timezone write fix This reverts commit 908666d253266a38382ce0f3abc1e1ee641cf75d. The fix edits ui-web/src/features/settings/SettingsPage.tsx, a file the ui-web rebuild on refactor/ui_web_architecture replaces with SettingsApp and its pages/ tree, so the change is redone on that branch rather than carried across the rewrite. Co-authored-by: Claude (claude-opus-5) --- i18n/messages.json | 8 +++ raven/rpc/methods/console.py | 25 +++++---- tests/test_rpc_settings.py | 53 ------------------- ui-tui/src/i18n/messages.generated.ts | 4 ++ .../features/settings/SettingsPage.test.tsx | 11 ---- ui-web/src/features/settings/SettingsPage.tsx | 13 +++++ 6 files changed, 37 insertions(+), 77 deletions(-) diff --git a/i18n/messages.json b/i18n/messages.json index 1dfe5ca86..2ad3c9ee5 100644 --- a/i18n/messages.json +++ b/i18n/messages.json @@ -4059,6 +4059,14 @@ "en": "Outbound behaviour", "zh": "外发行为" }, + "gui.set.chn.cron_ph": { + "zh": "如 telegram, feishu;* 表示全部", + "en": "e.g. telegram, feishu; * means all" + }, + "gui.set.chn.cron_to": { + "en": "Deliver schedules to", + "zh": "定时提醒投递到" + }, "gui.set.chn.entry": { "en": "Channels", "zh": "入口" diff --git a/raven/rpc/methods/console.py b/raven/rpc/methods/console.py index afd74ae12..f4e94ccdd 100644 --- a/raven/rpc/methods/console.py +++ b/raven/rpc/methods/console.py @@ -559,22 +559,21 @@ async def settings_set(params: dict, *, agent_loop_factory=None) -> dict: i18n.set_language(value) return {"applied": True, "previous": prev} - if key == "cron.defaultTimezone": + if key in ("cron.defaultTimezone", "cron.forwardChannels"): from raven.config.update import update_cron_config - if not isinstance(value, str) or not value: - raise ConfigValidationError("defaultTimezone must be a non-empty string") - from zoneinfo import ZoneInfo + sub = key.split(".", 1)[1] + if sub == "defaultTimezone": + if not isinstance(value, str) or not value: + raise ConfigValidationError("defaultTimezone must be a non-empty string") + from zoneinfo import ZoneInfo - try: - ZoneInfo(value) - except Exception: - raise ConfigValidationError(f"unknown timezone: {value}") from None - # The page names the key the way the config file spells it; the writer - # validates against ``CronConfig.model_fields``, which holds the Python - # field names. Nothing else in this endpoint crosses the two, so the - # conversion belongs here rather than in a writer the CLI shares. - sub = to_snake(key.split(".", 1)[1]) + try: + ZoneInfo(value) + except Exception: + raise ConfigValidationError(f"unknown timezone: {value}") from None + elif not isinstance(value, list) or not all(isinstance(x, str) for x in value): + raise ConfigValidationError("forwardChannels must be a list of strings") prev = update_cron_config(sub, value) return {"applied": True, "previous": prev} diff --git a/tests/test_rpc_settings.py b/tests/test_rpc_settings.py index 4a75f623b..a49c367c8 100644 --- a/tests/test_rpc_settings.py +++ b/tests/test_rpc_settings.py @@ -626,59 +626,6 @@ async def test_default_permission_mode_is_a_settings_key(cfg): await rpc_console.settings_set({"key": "permissions.mode", "value": "yolo"}) -class TestTheCronTimezoneControl: - """The settings page's timezone box, from the wire spelling to the loader. - - ``settings.set`` is handed the key as the page sends it -- the JSON - spelling ``cron.defaultTimezone`` -- while ``update_cron_config`` validates - against ``CronConfig.model_fields``, which holds Python field names. This - branch is the only place in the endpoint where the two conventions meet, so - a missing conversion here is a save that raises instead of writing. - """ - - async def test_the_timezone_round_trips_through_the_writer_and_the_loader(self, cfg): - from raven.config.loader import load_config - - r = await rpc_console.settings_set({"key": "cron.defaultTimezone", "value": "Asia/Tokyo"}) - - assert r["applied"] is True - assert _read(cfg)["cron"]["defaultTimezone"] == "Asia/Tokyo" - # Landing in the file is half of it: the writer spells the key one way - # and the loader has to read that same spelling back. Deliberately not a - # claim about scheduling -- ``CronConfig.default_timezone`` has exactly - # one consumer today, the ``raven cron config get`` display path, and - # ``_compute_next_run`` falls back to the machine's local zone rather - # than to this field. - assert load_config(cfg).cron.default_timezone == "Asia/Tokyo" - - async def test_the_previous_timezone_comes_back(self, cfg): - await rpc_console.settings_set({"key": "cron.defaultTimezone", "value": "Asia/Tokyo"}) - r = await rpc_console.settings_set({"key": "cron.defaultTimezone", "value": "UTC"}) - assert r["previous"] == "Asia/Tokyo" - - async def test_an_unknown_zone_is_refused_before_anything_is_written(self, cfg): - with pytest.raises(ConfigValidationError, match="unknown timezone"): - await rpc_console.settings_set({"key": "cron.defaultTimezone", "value": "Mars/Olympus"}) - assert "cron" not in _read(cfg) - - async def test_an_empty_timezone_is_refused(self, cfg): - with pytest.raises(ConfigValidationError): - await rpc_console.settings_set({"key": "cron.defaultTimezone", "value": ""}) - - -async def test_the_retired_forward_channels_key_is_not_writable(cfg): - """``forward_channels`` left ``CronConfig`` when delivery became fire-at-origin. - - The loader strips both spellings out of a config file on the way in, so no - field stands behind this key any more. It has to be refused the way every - other unwritable key is -- typed, and naming itself -- rather than reaching - a writer whose only possible answer is to raise. - """ - with pytest.raises(ConfigValidationError, match="not writable"): - await rpc_console.settings_set({"key": "cron.forwardChannels", "value": ["telegram"]}) - assert "cron" not in _read(cfg) - - async def test_extension_pin_writes_roundtrip_through_raven_loader(cfg): from raven.config.raven import load_raven_config from raven.config.update_providers import set_provider_fields diff --git a/ui-tui/src/i18n/messages.generated.ts b/ui-tui/src/i18n/messages.generated.ts index 8b5b993f7..0ef4e3c61 100644 --- a/ui-tui/src/i18n/messages.generated.ts +++ b/ui-tui/src/i18n/messages.generated.ts @@ -1208,6 +1208,8 @@ export const UI_TEXT: Record> = { 'gui.set.checking': 'Checking...', 'gui.set.chn.all': 'every channel', 'gui.set.chn.card': 'Outbound behaviour', + 'gui.set.chn.cron_ph': 'e.g. telegram, feishu; * means all', + 'gui.set.chn.cron_to': 'Deliver schedules to', 'gui.set.chn.entry': 'Channels', 'gui.set.chn.entry_w': 'Add or remove a channel in its own module.', 'gui.set.chn.hints': 'Send tool hints', @@ -2934,6 +2936,8 @@ export const UI_TEXT: Record> = { 'gui.set.checking': '检查中…', 'gui.set.chn.all': '全部渠道', 'gui.set.chn.card': '外发行为', + 'gui.set.chn.cron_ph': '如 telegram, feishu;* 表示全部', + 'gui.set.chn.cron_to': '定时提醒投递到', 'gui.set.chn.entry': '入口', 'gui.set.chn.entry_w': '增删渠道在自己的模块里。', 'gui.set.chn.hints': '发送工具提示', diff --git a/ui-web/src/features/settings/SettingsPage.test.tsx b/ui-web/src/features/settings/SettingsPage.test.tsx index 1fd9e719a..8b7267dce 100644 --- a/ui-web/src/features/settings/SettingsPage.test.tsx +++ b/ui-web/src/features/settings/SettingsPage.test.tsx @@ -454,17 +454,6 @@ describe('settings island', () => { expect(calls).toContainEqual(['set', { key: 'cron.defaultTimezone', value: 'UTC' }]) }) - it('offers no row for the retired cron forward-channels key', async () => { - install() - await mount() - await act(async () => { - screen.getByText('gui.set.pg.channel').click() - }) - // The field left CronConfig when delivery became fire-at-origin, and - // settings.set refuses the key outright, so a row here could only error. - expect(screen.queryByText('gui.set.chn.cron_to')).toBeNull() - }) - it('resets an out-of-range number without writing, and writes a valid one', async () => { const { calls } = install() await mount() diff --git a/ui-web/src/features/settings/SettingsPage.tsx b/ui-web/src/features/settings/SettingsPage.tsx index 60984c535..f7909bc14 100644 --- a/ui-web/src/features/settings/SettingsPage.tsx +++ b/ui-web/src/features/settings/SettingsPage.tsx @@ -2425,11 +2425,24 @@ function ExecPage({ s }: { s: SettingsState }): JSX.Element { function ChannelPage({ s }: { s: SettingsState }): JSX.Element { const sh = shell() const raw = s.snap.raw + const fwd = (V(raw, 'cron.forwardChannels', []) as string[]) || [] return ( <> + + v + .split(',') + .map((x) => x.trim()) + .filter(Boolean) + } + />