Commit Graph

341 Commits

Author SHA1 Message Date
Yufan You 5589b89478
fix(cache): call JSON.stringify when storing value in cache.tryGet (#20671)
* fix(cache): call JSON.stringify when storing value in cache.tryGet

The value is returned as-is, so we may assume that it's not JSON
stringified inside `getValueFunc`. Otherwise, the caller already needs
to distinguish between values that are either JSON parsed or not.

* Revert "fix(routes/mastodon): don't refresh account ID cache (#20663)"

This reverts commit c2cff410fa.

* test: add test

* test: lint
2026-05-07 01:26:11 +08:00
DIYgod 90b81b51fc
fix: connect playwright ws endpoint via protocol (#21917) 2026-05-05 22:17:25 +08:00
DIYgod 566f028aaf
refactor: migrate browser automation to Playwright (#21901)
* refactor: migrate browser automation to Playwright

* ci: update browser checks for Playwright
2026-05-04 21:16:18 +08:00
dependabot[bot] bb5a944b35
chore(deps-dev): bump tsdown from 0.21.9 to 0.21.10 (#21810)
* chore(deps-dev): bump tsdown from 0.21.9 to 0.21.10

Bumps [tsdown](https://github.com/rolldown/tsdown) from 0.21.9 to 0.21.10.
- [Release notes](https://github.com/rolldown/tsdown/releases)
- [Commits](https://github.com/rolldown/tsdown/compare/v0.21.9...v0.21.10)

---
updated-dependencies:
- dependency-name: tsdown
  dependency-version: 0.21.10
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* fix: migrate tsdown options

* test: fix flaky test

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-29 05:09:46 +08:00
DIYgod 7c2bcf5bdf
feat: add HTTP cache (#21814) 2026-04-24 11:35:51 +08:00
github-actions[bot] ebfe021b50 style: auto format 2026-04-14 15:15:05 +00:00
Tony 2897f515a3
feat: disable IPv6 (#21719) 2026-04-13 23:45:08 +08:00
dependabot[bot] ecd202c980
chore(deps-dev): bump got from 14.6.6 to 15.0.0 (#21591)
* chore(deps-dev): bump got from 14.6.6 to 15.0.0

Bumps [got](https://github.com/sindresorhus/got) from 14.6.6 to 15.0.0.
- [Release notes](https://github.com/sindresorhus/got/releases)
- [Commits](https://github.com/sindresorhus/got/compare/v14.6.6...v15.0.0)

---
updated-dependencies:
- dependency-name: got
  dependency-version: 15.0.0
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

* chore(deps): remove form-data dependency

* chore: make fakeGot function async

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Tony <TonyRL@users.noreply.github.com>
2026-04-10 00:57:30 +08:00
MMA 6288f06b97
fix(route/dailypush): use puppeteer (#21160)
* use puppeteer

* fix typo

* fix fetch page one by one

* use deprecated method

* bypass Disallow await inside of loops

* use one browser for the whole route

* bypass Disallow await inside of loops

* use one browser even if marked deprecated by doc

* use Promise.all()

* fix new destory -> destroy
2026-04-04 05:00:09 +08:00
Tony 0f705a566c style: use oxlint-plugin-eslint for unimplemented eslint rules 2026-04-02 05:29:27 +08:00
Tony a61dc787e6
fix(utils/wechat-mp): fix APP_MSG_PAGE metadata fetching (#21577)
* fix(utils/wechat-mp): fix APP_MSG_PAGE metadata fetching

* fix(tests/wechat-mp): update HTML string formatting in ExtractMetadata test
2026-04-01 06:00:17 +08:00
Tony a2861cdd4f
fix: update rss-parser patch to support other compressions (#21561) 2026-03-30 22:22:24 +00:00
Tony 7ab0d1eff4
fix(ua): ensure header-generator and custom ua are effective with each other (#21546)
* fix(ua): ensure header-generator and custom ua are effective with each other

* fix(ua): review comment
2026-03-29 22:47:56 +08:00
Tony a48ad7b4ae chore: bump transitive deps
brace-expansion
2026-03-28 01:43:37 +08:00
Tony 5ae7432b26
fix(ofetch): use late-binding fetch in ofetch to ensure request-rewriter intercepts bundled builds (#21518) 2026-03-26 21:24:46 +08:00
dependabot[bot] df6b73d866
chore(deps-dev): bump msw from 2.4.3 to 2.12.14 (#21483)
* chore(deps-dev): bump msw from 2.4.3 to 2.12.14

Bumps [msw](https://github.com/mswjs/msw) from 2.4.3 to 2.12.14.
- [Release notes](https://github.com/mswjs/msw/releases)
- [Changelog](https://github.com/mswjs/msw/blob/main/CHANGELOG.md)
- [Commits](https://github.com/mswjs/msw/compare/v2.4.3...v2.12.14)

---
updated-dependencies:
- dependency-name: msw
  dependency-version: 2.12.14
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* chore: remove supertest

MSW's setupServer() works by patching Node's http module (via @mswjs/interceptors) to intercept outgoing requests. supertest's requests to http://127.0.0.1:1200 go through the same patched http module which causes server bad request

* chore(patch): add patch for @mswjs/interceptors@0.41.3

* fix(server): update server listen options to bypass unhandled requests

* chore: update patch for @mswjs/interceptors

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Tony <TonyRL@users.noreply.github.com>
2026-03-25 02:31:42 +08:00
Tony 86088852ab
test(header-generator): improve header validation (#21492) 2026-03-24 23:37:28 +08:00
Tony a3f06e485c
feat: esm directory import (#21428) 2026-03-18 15:15:28 +08:00
Tony c09dacac5b
feat(api): add cache probing (#21300)
* feat(api): add cache probing

* fix(tests): correct query parameter from 'path' to 'requestPath' in status tests

* feat(cache): add has to workers kv
2026-03-05 02:36:53 +08:00
Tony 1acb805799
style: migrate eslint to oxlint (#21224)
* chore: add oxlint support

* style: apply autofix

* style: apply autofix for toSorted

* style: use alternative api for nsfw-flag jsPlugins

* fix: git diff

* fix: codeql

* test: console.log

* style: update lint workflow to use Checkstyle SARIF converter

* style: remove debug log from extractDateFromURL function

* chore: use oxlint-json-to-sarif

* test: console.log

* test: remove console.log

* test: update cookie data expectation in getCookies test

https://github.com/mccutchen/go-httpbin/pull/235 breaks this

* style: type-aware linting

* style: enable type-aware linting for oxlint
2026-02-28 21:23:11 +08:00
Ethan Shen f4130d4c1a
fix(utils): refactor parseRelativeDate to support dynamic reference (#21054)
* fix(utils): refactor parseRelativeDate to support dynamic reference and semantic formats

* test: add issue test case

---------
2026-02-06 21:44:49 +08:00
DIYgod d40f812563
refactor: deploy all routes to Cloudflare Workers (#20809)
- Remove Worker-specific route filtering (worker_namespaces) to include all 1537 namespaces in Worker builds
- Remove popularity analytics logic (foloAnalysisTop100) as Worker can now handle all routes
- Update node:child_process alias in tsdown config to use dedicated shim file
- Inline child_process shim in node-module.ts to avoid import cycles
- Fix tough-cookie import in telecompaper route (use named import CookieJar)
- Remove ESModule rules from wrangler.toml (not needed)

This enables full RSSHub functionality on Cloudflare Workers with no memory/size limitations.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com>
2026-01-04 17:33:55 +08:00
DIYgod 7b96e82f1c
feat(worker): implement KV-based caching for Cloudflare Workers (#20806)
- Add Cloudflare Workers KV cache implementation with TTL support
- Create xxhash-wasm shim using Web Crypto API to replace incompatible WebAssembly
- Unify cache middleware to use single cache.ts via tsdown-worker.config.ts alias
- Update dependencies: wrangler, @cloudflare/puppeteer, @cloudflare/workers-types
- Configure automatic KV namespace provisioning without manual ID configuration

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com>
2026-01-04 11:29:33 +08:00
DIYgod 1131350b5f
feat: deploy RSSHub to Cloudflare Workers (#20804)
* feat: deploy RSSHub to Cloudflare Workers

- Create Worker entry point (lib/worker.ts) with polyfills
- Implement Worker-specific app configuration (lib/app.worker.tsx)
- Add automatic .worker.ts resolution plugin for cleaner config
- Simplify build configuration with only 3 essential shims
- Enable static asset serving via Cloudflare Static Assets feature
- Support dynamic route loading with proper module aliasing

🤖 Generated with Claude Code

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>

* feat(worker): add puppeteer and request-rewriter support

- Add @cloudflare/puppeteer for Browser Rendering API support
- Create puppeteer.worker.ts with Cloudflare Browser binding
- Add request-rewriter Worker version with static browser headers
- Add vm module shim to node-module.ts for JSDOM compatibility
- Configure __dirname/__filename in tsdown for CommonJS compat
- Update wrangler.toml with BROWSER binding configuration
- Dynamically extract namespaces from foloAnalysisTop100 for routes

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(cache): restore synchronous initialization for tests

The async initialization caused cache tests to fail because the cache
module wasn't ready when tests ran. Restored synchronous imports while
keeping Worker-specific no-op behavior.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* test(worker): add automated Worker integration tests

- Add miniflare for Worker environment simulation
- Create lib/worker.worker.test.ts with integration tests
- Test basic routes (/test/1, /, unknown routes)
- Test RSS feed routes (hackernews, v2ex)
- Test error handling for puppeteer routes without BROWSER binding
- Add worker-test npm script

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* refactor(worker): use test routes for Worker integration tests

- Replace third-party routes (hackernews, v2ex, weibo) with /test/* routes
- Remove miniflare dependency (wrangler includes it internally)
- Simplify test setup using wrangler's unstable_dev API

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* fix(test): make header-generator test less flaky

The header-generator library may return different platform values due to
internal randomness. Update test to check for valid format instead of
exact value.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* fix(test): exclude worker tests from vitest coverage run

Worker tests require the dist-worker bundle to be built first,
which is not part of the regular CI test workflow. These tests
should be run separately using 'pnpm worker-test'.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* test(worker): add automated Worker integration tests

- Add worker-build step to CI workflow before running tests
- Revert exclusion of worker tests from vitest coverage run
- Worker tests now run as part of the regular test suite

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(worker): fix routes.js alias path in worker build config

Change alias key from absolute path to relative import path to match
how it's imported in lib/registry.ts. This fixes the worker build
failing with "Could not resolve '../assets/build/routes.js'" error.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* ci: trigger CI re-run

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(ci): build worker routes before worker-build

The worker build requires routes-worker.js which is generated by
running build:routes with WORKER_BUILD=true. This was missing in CI,
causing the worker build to fail with "Could not resolve routes.js".

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(test): increase timeout for Worker integration tests

Worker tests need more time in CI environments. Increase individual
test timeouts from default 10s to 30s for each test case.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com>
2026-01-04 09:22:05 +08:00
github-actions[bot] 2f37d73a24 style: auto format 2026-01-02 01:44:18 +00:00
Tony b84bb92a8a
style: replace unicorn/no-for-loop w/ typescript/prefer-for-of (#20798)
* style: replace unicorn/no-for-loop w/ typescript/prefer-for-of

* style: fix typescript-eslint/prefer-for-of

* style: fix typescript/no-inferrable-types

* style: fix typescript-eslint/consistent-generic-constructors

* style: typescript-eslint/consistent-type-assertions

* style: fix typescript-eslint/array-type

* fix: revert .entries() in d96ca62d3d80806b0475a54e749decfc07b24701
2026-01-02 09:42:24 +08:00
DIYgod 81ff849337
feat(tests): add comprehensive unit tests for various utility functions and views (#20779)
* feat(tests): add comprehensive unit tests for various utility functions and views

- Implement tests for cache utility to ensure proper behavior with no cache and TTL keys.
- Add tests for common utilities including string manipulation and path handling.
- Introduce tests for directory import functionality to validate file imports and pattern matching.
- Create tests for git hash retrieval to handle fallback scenarios.
- Develop tests for deprecated got utility to verify response handling and retry logic.
- Enhance got utility tests to include request hooks and search parameter handling.
- Mock header generator tests to validate user agent handling.
- Expand helpers tests to cover current path retrieval and duration parsing.
- Implement tests for ofetch utility to ensure proper proxy handling and logging.
- Add OpenTelemetry metric tests to validate metric serialization.
- Create proxy tests to verify multi-proxy selection and failure handling.
- Introduce request rewriter tests to validate fetch and get wrapper functionality.
- Add timezone utility tests to handle various input types.
- Implement view tests for Atom and RSS rendering to ensure correct output.
- Create index view tests to validate debug information display based on configuration.

* refactor: remove deprecated got implementation and associated tests

* test: expand coverage for api, middleware, and utils
2025-12-29 17:34:48 +08:00
DIYgod cf4e772de8
refactor: replace art template with jsx (#20777)
* refactor: replace art template with jsx

* fix: tsx files

* fix: renderToString import

* fix: hono html raw import

* feat: remove art
2025-12-29 12:27:32 +08:00
Dzming Li dbb692462d
chore: add Nix flake and NixOS module support (#20597)
* feat(nix): add flake module and offline-friendly build

* chore(nix): upgrade to Node.js 24 and pnpm 10

Update devenv.nix to match project dependencies:
- Upgrade Node.js from 22 to 24 (aligns with Dockerfile node:24-bookworm)
- Upgrade pnpm from 9 to 10 (aligns with package.json pnpm@10.22.0)

* test: fix buffer-get test timeout

Replace external URL with mock server endpoint to prevent test timeout.
The test was trying to fetch from http://example.com which is not mocked,
causing it to timeout. Now uses http://rsshub.test/headers which is
properly mocked in the test setup.
2025-12-09 19:43:06 +08:00
github-actions[bot] edfac9f24c style: auto format 2025-11-27 05:54:33 +00:00
github-actions[bot] 838eb3c7ff style: auto format 2025-11-27 05:49:33 +00:00
github-actions[bot] a28890a135 style: auto format 2025-11-27 02:55:46 +00:00
Tony 2ae10a7a02
feat: replace rand-user-agent with header-generator (#20480)
* Initial plan

* Replace @tonyrl/rand-user-agent with header-generator and add sec-ch-* headers

Co-authored-by: TonyRL <11386903+TonyRL@users.noreply.github.com>

* Extract unwanted UA validation into standalone isValidUserAgent function

Co-authored-by: TonyRL <11386903+TonyRL@users.noreply.github.com>

* Add headerGeneratorPreset option to got and ofetch utilities

* Replace randUserAgent usage in lib/routes with headerGeneratorPreset option

Co-authored-by: TonyRL <11386903+TonyRL@users.noreply.github.com>

* chore: pin header-generator to exact match

* fix: replace randUserAgent with a static user agent string in config

* Remove deprecated _randUserAgent function and related tests

Co-authored-by: TonyRL <11386903+TonyRL@users.noreply.github.com>

* refactor: rename to header-generator

* Changes before error encountered

Co-authored-by: TonyRL <11386903+TonyRL@users.noreply.github.com>

* fix: update generateHeaders parameter type to Partial<HeaderGeneratorOptions>

* fix: add ios preset

* fix: rename headerGeneratorPreset to headerGeneratorOptions

* feat: add MODERN_IOS preset and update header-generator patch hash

* fix: use ios preset

* fix: update FetchOptions to include headerGeneratorOptions type

* refactor: simplify header generation and update header inclusion list

* fix: remove header-generator patch

* refactor: update user-agent handling in requests and tests

* test: remove redundant headerGeneratorOptions test and add new ofetch header preset test

* test: add custom user-agent test for ofetch

* test: update user-agent expectation in ofetch headers test

* Update lib/utils/ofetch.ts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* refactor: replace hardcoded headers with generatedHeaders in fetch and get request handlers

* refactor: rename test suite from rand-user-agent to header-generator and remove unused dependency

* Update lib/utils/header-generator.ts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: TonyRL <11386903+TonyRL@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-11-14 00:49:41 +08:00
dependabot[bot] 52c18b7f25
chore(deps-dev): bump eslint-plugin-unicorn from 61.0.2 to 62.0.0 (#20381)
* chore(deps-dev): bump eslint-plugin-unicorn from 61.0.2 to 62.0.0

Bumps [eslint-plugin-unicorn](https://github.com/sindresorhus/eslint-plugin-unicorn) from 61.0.2 to 62.0.0.
- [Release notes](https://github.com/sindresorhus/eslint-plugin-unicorn/releases)
- [Commits](https://github.com/sindresorhus/eslint-plugin-unicorn/compare/v61.0.2...v62.0.0)

---
updated-dependencies:
- dependency-name: eslint-plugin-unicorn
  dependency-version: 62.0.0
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

* style: fix unicorn/prefer-string-raw

* style: fix unicorn/no-useless-collection-argument

* style: fix unicorn/no-immediate-mutation

* style: fix unicorn/no-immediate-mutation

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-28 01:26:56 +08:00
Stephen Zhou f8c1415b8e
fix(route/bilibili): duration for video route 2025-10-09 10:44:18 +08:00
Tony 49685ad6b3
fix(ofetch): include error cause in request error logging (#20225) 2025-10-04 04:50:53 +08:00
dependabot[bot] 802cbf95d2
chore(deps-dev): bump eslint-plugin-unicorn from 60.0.0 to 61.0.2 (#19998)
* chore(deps-dev): bump eslint-plugin-unicorn from 60.0.0 to 61.0.1

Bumps [eslint-plugin-unicorn](https://github.com/sindresorhus/eslint-plugin-unicorn) from 60.0.0 to 61.0.1.
- [Release notes](https://github.com/sindresorhus/eslint-plugin-unicorn/releases)
- [Commits](https://github.com/sindresorhus/eslint-plugin-unicorn/compare/v60.0.0...v61.0.1)

---
updated-dependencies:
- dependency-name: eslint-plugin-unicorn
  dependency-version: 61.0.1
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

* refactor: replace `replace(/regex/g)` with replaceAll

* refactor: replace sort() with toSorted() for immutability in various routes and utilities

* refactor: fix unicorn/prefer-array-find

* refactor: fix unicorn/prefer-at

* refactor: add unicorn/no-array-sort rule to ESLint configuration

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-08 23:03:48 +08:00
DIYgod 532619a8c2
fix(tests): update expected values and increase timeout for Puppeteer tests
- Adjusted expected values in error handling tests to reflect updated application behavior.
- Increased timeout for Puppeteer tests from 20 seconds to 45 seconds to accommodate longer loading times.
2025-08-22 17:45:39 +08:00
DIYgod bbe768246f
feat: vercel compatibility 2025-08-11 21:39:03 +08:00
dependabot[bot] ae3e646ea9
chore(deps-dev): bump eslint-plugin-unicorn from 59.0.1 to 60.0.0 (#19662)
* chore(deps-dev): bump eslint-plugin-unicorn from 59.0.1 to 60.0.0

---
updated-dependencies:
- dependency-name: eslint-plugin-unicorn
  dependency-version: 60.0.0
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

* refactor: replace reverse() with toReversed() for improved readability

* chore: update packageManager version to pnpm@10.13.1

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-22 20:45:42 +08:00
Tony 857d5989c1
feat(utils/puppeteer): replace `puppeteer-extra-plugin-stealth` with `rebrowser` (#19506)
* feat: use rebrowser

* fix: remove rebrowser-puppeteer-core

* fix: chrome not found

* fix: replace remove-infobar with disable-blink-features

remove-infobar was removed since M76 https://github.com/puppeteer/puppeteer/issues/1765

* feat: remove outdated stealth plugin

* fix: remove puppeteer non stealth test
2025-07-01 04:38:05 +08:00
pseudoyu 3d9ac5c6ee
feat(lib/proxy): support multiple proxier by `PROXY_URIS` env 2025-06-24 11:05:14 +08:00
github-actions[bot] 8abc00fe2e style: auto format 2025-06-22 16:44:52 +00:00
夜阑听风 90eb7bbacf
feat: support float score threshold for douban (#19425)
* support float score threshold for douban

* add readable-social tests
2025-06-23 00:42:44 +08:00
纪华裕 401c0c563f
feat(cache): enhance tryGet method to support generic return types (#19261) 2025-06-03 09:37:43 +08:00
Tony 5cf663659a fix: add 'networkidle2' option to waitUntil in getPuppeteerPage 2025-05-29 04:02:55 +08:00
DIYgod cf8e3bdf45
feat: bilibili new puppeteer 2025-05-28 22:07:44 +08:00
DIYgod 27305f107a
test: fix puppeteer 2025-05-28 21:26:06 +08:00
DIYgod bf3649324b
fix: puppeteer proxy regex 2025-05-28 21:12:21 +08:00
DIYgod 34c43eaa0d
fix: puppeteer proxy 2025-05-28 12:51:43 +08:00