Modernize web app: Vite + React 18, Python 3.11 runtime#44
Conversation
Replace the ejected CRA1 / webpack3 / babel6 toolchain (built on the EOL node:8 / Debian-stretch base) with Vite 5 + React 18, and move the Flask service to a Python 3.11 runtime. This clears the build-toolchain and Python security alerts that the dependency bundle (#43) could not, and gets off archived/unsupported base images. Frontend: - Vite 5 + @vitejs/plugin-react; build output stays in react_app/build (served by app.py unchanged) - React 16.5 -> 18.3 (createRoot) - react-router 4 -> 6 (Routes/element); a small withRouter shim keeps the class components' history/location usage working - react-intl 2 -> 6; FormattedHTMLMessage/FormattedRelative shims (both removed upstream) preserve the existing behaviour - react-redux 5 -> 8; dropped connected-react-router - node-sass 4 -> dart-sass; react-html-parser -> html-react-parser; remarkable 1 -> 2; typeface-roboto -> @fontsource/roboto - axios 0.18 -> 1.x, immutable 3 -> 4 - Tests migrated jest -> vitest (+ a render smoke test); snapshots regenerated (also fixes the long-stale steemit.com fixture) Backend / runtime: - Dockerfile-web is now multi-stage: node:20 builds the SPA, python:3.11 runs Flask/gunicorn - requirements.txt bumped to a modern, fully-patched set (Flask 3, Werkzeug 3, requests 2.32, urllib3 2.2, certifi 2024.x, ...) - replaced the unmaintained xonfig (crashes on Python 3.10+) with a small stdlib configparser reader (appconfig.py), same get_option API + config Verified: Vite build, vitest (16/16), full multi-stage Docker image build, and a runtime smoke test (gunicorn serves the SPA; routes + assets 200).
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: aae8ed328f
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| six==1.16.0 | ||
| urllib3==2.2.3 | ||
| Werkzeug==3.0.4 | ||
| WTForms==3.1.2 |
There was a problem hiding this comment.
Keep the counter's config dependency available
After this dependency list drops xonfig, the counter image is broken: Dockerfile-counter still installs this same requirements.txt and runs counter/main.py, which imports from xonfig import get_option. Any deployment that starts the counter container will exit with ModuleNotFoundError before it can update doc_count in Redis.
Useful? React with 👍 / 👎.
| if not key.startswith('__ENV__') or len(key) == len('__ENV__'): | ||
| continue | ||
| try: | ||
| section, option = key[len('__ENV__'):].split('_', 1) |
There was a problem hiding this comment.
Parse env overrides without dropping underscored sections
In environments that override config via variables such as __ENV__ESEARCH_API_URL, this split stores the value under section ESEARCH and option API_URL, but app.py reads get_option('ESEARCH_API', 'URL') and the sample config defines [ESEARCH_API]. Those production API URL/token/timeout overrides are therefore ignored and the app falls back to file config.
Useful? React with 👍 / 👎.
| const title = entry.title_marked ? parse(markedHtml(entry.title_marked)) : markedHtml(entry.title); | ||
| const body = entry.body_marked ? parse(markedHtml(entry.body_marked)) : markedHtml(entry.body); |
There was a problem hiding this comment.
Preserve sanitizing when parsing highlighted snippets
For search results containing escaped user text like <img src=x> in a marked title/body, markedHtml() strips tags before its final he.decode, so decoding can reintroduce real tags after sanitization. The previous ReactHtmlParser transform removed every parsed tag except <mark>, but these new parse(...) calls render whatever tags are reintroduced, allowing untrusted snippets to inject unexpected DOM into result links.
Useful? React with 👍 / 👎.
- counter/main.py + Dockerfile-counter: the counter still imported xonfig and ran on EOL ubuntu:18.04 (py3.6), so it would break with the modernized requirements. Switch it to appconfig and the python:3.11 base, matching the web image. (P1) - appconfig: resolve __ENV__ overrides against the longest known section name so sections containing underscores (e.g. ESEARCH_API) match correctly, keeping first-underscore split as a fallback. (P2) - list-item: markedHtml() runs he.decode() after stripping tags, which can turn escaped user content back into real tags; restore non-<mark> tag stripping via html-react-parser's replace (keyed on node.name so <script>/<style> are caught too) and add a regression test. (P2)
Why
The web app was an ejected CRA 1.x (webpack 3 / babel 6 / node-sass 4) built on the EOL
node:8/ Debian-stretch / Python 3.5 base. That base is what blocked the remaining security updates after the dependency bundle (#43): the build-toolchain alerts (webpack/babel/loader-utils/handlebars-in-webpack/…), the two build-breaking Dependabot PRs, and the Python deps whose patched releases dropped 3.5. This migration clears that whole class and gets us off archived base images.Frontend
@vitejs/plugin-reactreplaces the ejected webpack/babel config (config/,scripts/removed). Build output stays inreact_app/build, soapp.pyserves it unchanged.createRoot).Routes/element). A smallwithRoutershim reconstructs the v4-stylehistory/locationprops the class components use, so the screens didn't need rewriting.FormattedHTMLMessageandFormattedRelativewere removed upstream — small compat shims preserve the existing behaviour.connected-react-router.steemit.comlinkify fixture).Backend / runtime
Dockerfile-webis now multi-stage:node:20builds the SPA,python:3.11-slimruns Flask/gunicorn (image ~155 MB).requirements.txtbumped to a modern, fully-patched set (Flask 3, Werkzeug 3, requests 2.32, urllib3 2.2, certifi 2024.x, …) — this is what lets the Python Dependabot PRs (Bump jinja2 from 2.10.1 to 2.11.3 #16 Bump flask-cors from 3.0.6 to 3.0.9 #18 Bump urllib3 from 1.24.2 to 1.26.5 #23 Bump certifi from 2018.8.24 to 2022.12.7 #38) resolve.xonfig(crashes on Python 3.10+) with a tiny stdlibconfigparserreader (appconfig.py) — sameget_optionAPI and config-file behaviour (incl.ast.literal_evaltyping and__ENV__overrides).Verification (all on this PR's code)
yarn build(Vite, node:20) ✅vitest run✅ 16/16 (unit + render smoke)GET /serves the SPA,/api-docs(SPA fallback) + hashed JS asset + favicon all return 200 ✅app.pyimports on Python 3.11 with typed config values ✅Notes for review
/api/*flow (needs redis + the search backend) or click through in a real browser; a quick look on staging/preview before deploy is worth it.