docs: complete migration documentation and finalize dev environment

- Add comprehensive CHANGELOG.md with all upgrade phases and results
- Update TODO.md to mark framework upgrades as completed
- Update UPGRADE_PLAN with actual timelines and detailed phase results
- Finalize docker-compose.override.yml and Dockerfile.dev for dev workflow
- Update package-lock.json after dependency changes

All 4 phases completed successfully:
- Backend: Node 14 → 24
- Frontend: React 17 → 18.3.1, Router v5 → v6, MUI v4 → v5
- Dev environment with HMR fully functional
- Integration smoke tests passed
This commit is contained in:
Matthias Lotz 2025-10-29 23:11:37 +01:00
parent fe65544893
commit 80ffcfd210
3 changed files with 94 additions and 233 deletions

View File

@ -38,6 +38,22 @@ services:
- image-uploader-internal
depends_on:
- image-uploader-backend
image-uploader-backend:
container_name: image-uploader-backend-dev
build:
context: ./backend
dockerfile: Dockerfile
working_dir: /usr/src/app
ports:
- "5000:5000"
volumes:
- ./backend:/usr/src/app:cached
- backend_node_modules:/usr/src/app/node_modules
environment:
- NODE_ENV=development
networks:
- image-uploader-internal
command: [ "npm", "run", "server" ]
# The Dockerfile.dev provides a proper CMD that starts nginx and the
# react dev server; no ad-hoc command is required here.
@ -49,4 +65,6 @@ networks:
volumes:
node_modules:
driver: local
backend_node_modules:
driver: local

View File

@ -1,4 +1,4 @@
FROM node:16-bullseye
FROM node:18-bullseye
# Install nginx and bash
RUN apt-get update \
@ -22,7 +22,8 @@ RUN chown appuser:appuser /app || true
USER appuser
# Install dependencies as non-root (faster overall because we avoid chown -R)
RUN npm ci --legacy-peer-deps --no-audit --no-fund
# Use npm ci without legacy peer deps to get a clean, reproducible install
RUN npm ci --no-audit --no-fund
# Switch back to root to add the start script and adjust nginx paths
USER root

View File

@ -12,17 +12,16 @@
"@emotion/styled": "^11.11.0",
"@mui/icons-material": "^5.14.0",
"@mui/material": "^5.14.0",
"@mui/styles": "^5.14.0",
"@testing-library/jest-dom": "^5.11.9",
"@testing-library/react": "^11.2.5",
"@testing-library/user-event": "^12.8.3",
"axios": "^0.21.1",
"lottie-react": "^2.4.0",
"react": "^18.3.1",
"react-code-blocks": "^0.0.8",
"react-dom": "^18.3.1",
"react-dropzone": "^11.3.1",
"react-helmet": "^6.1.0",
"react-lottie": "^1.2.3",
"react-router-dom": "^6.28.0",
"react-scripts": "5.0.1",
"sass": "^1.32.8",
@ -2351,11 +2350,6 @@
"resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.10.0.tgz",
"integrity": "sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg=="
},
"node_modules/@emotion/serialize/node_modules/csstype": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="
},
"node_modules/@emotion/sheet": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.4.0.tgz",
@ -3791,11 +3785,6 @@
"node": ">=6"
}
},
"node_modules/@mui/material/node_modules/csstype": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="
},
"node_modules/@mui/material/node_modules/react-is": {
"version": "19.2.0",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-19.2.0.tgz",
@ -3859,69 +3848,6 @@
}
}
},
"node_modules/@mui/styled-engine/node_modules/csstype": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="
},
"node_modules/@mui/styles": {
"version": "5.18.0",
"resolved": "https://registry.npmjs.org/@mui/styles/-/styles-5.18.0.tgz",
"integrity": "sha512-GDgFUfl2MMN8iGrYTuhJ0hHRoja2kVOlXnHb5d3BBQCHFxOBaAPDKQxaNkoAwRf/vBhhwXWDsJA2XlkNHUPBgA==",
"dependencies": {
"@babel/runtime": "^7.23.9",
"@emotion/hash": "^0.9.1",
"@mui/private-theming": "^5.17.1",
"@mui/types": "~7.2.15",
"@mui/utils": "^5.17.1",
"clsx": "^2.1.0",
"csstype": "^3.1.3",
"hoist-non-react-statics": "^3.3.2",
"jss": "^10.10.0",
"jss-plugin-camel-case": "^10.10.0",
"jss-plugin-default-unit": "^10.10.0",
"jss-plugin-global": "^10.10.0",
"jss-plugin-nested": "^10.10.0",
"jss-plugin-props-sort": "^10.10.0",
"jss-plugin-rule-value-function": "^10.10.0",
"jss-plugin-vendor-prefixer": "^10.10.0",
"prop-types": "^15.8.1"
},
"engines": {
"node": ">=12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/mui-org"
},
"peerDependencies": {
"@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
"react": "^17.0.0 || ^18.0.0 || ^19.0.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@mui/styles/node_modules/@emotion/hash": {
"version": "0.9.2",
"resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz",
"integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g=="
},
"node_modules/@mui/styles/node_modules/clsx": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
"integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
"engines": {
"node": ">=6"
}
},
"node_modules/@mui/styles/node_modules/csstype": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="
},
"node_modules/@mui/system": {
"version": "5.18.0",
"resolved": "https://registry.npmjs.org/@mui/system/-/system-5.18.0.tgz",
@ -3969,11 +3895,6 @@
"node": ">=6"
}
},
"node_modules/@mui/system/node_modules/csstype": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="
},
"node_modules/@mui/types": {
"version": "7.2.24",
"resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.24.tgz",
@ -4940,6 +4861,15 @@
"resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz",
"integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ=="
},
"node_modules/@types/react": {
"version": "19.2.2",
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.2.tgz",
"integrity": "sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA==",
"peer": true,
"dependencies": {
"csstype": "^3.0.2"
}
},
"node_modules/@types/react-transition-group": {
"version": "4.4.12",
"resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.12.tgz",
@ -6290,23 +6220,6 @@
"babel-plugin-transform-react-remove-prop-types": "^0.4.24"
}
},
"node_modules/babel-runtime": {
"version": "6.26.0",
"license": "MIT",
"dependencies": {
"core-js": "^2.4.0",
"regenerator-runtime": "^0.11.0"
}
},
"node_modules/babel-runtime/node_modules/core-js": {
"version": "2.6.12",
"hasInstallScript": true,
"license": "MIT"
},
"node_modules/babel-runtime/node_modules/regenerator-runtime": {
"version": "0.11.1",
"license": "MIT"
},
"node_modules/balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
@ -7210,15 +7123,6 @@
"node": ">=0.10.0"
}
},
"node_modules/css-vendor": {
"version": "2.0.8",
"resolved": "https://registry.npmjs.org/css-vendor/-/css-vendor-2.0.8.tgz",
"integrity": "sha512-x9Aq0XTInxrkuFeHKbYC7zWY8ai7qJ04Kxd9MnvbC1uO5DagxoHQjm4JvG+vCdXOoFtCjbL2XSZfxmoYa9uQVQ==",
"dependencies": {
"@babel/runtime": "^7.8.3",
"is-in-browser": "^1.0.2"
}
},
"node_modules/css-what": {
"version": "3.4.2",
"license": "BSD-2-Clause",
@ -7385,6 +7289,11 @@
"resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz",
"integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg=="
},
"node_modules/csstype": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="
},
"node_modules/damerau-levenshtein": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz",
@ -7690,11 +7599,6 @@
"csstype": "^3.0.2"
}
},
"node_modules/dom-helpers/node_modules/csstype": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="
},
"node_modules/dom-serializer": {
"version": "0.2.2",
"license": "MIT",
@ -9552,6 +9456,19 @@
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="
},
"node_modules/fsevents": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
"hasInstallScript": true,
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
"node_modules/function-bind": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
@ -10263,11 +10180,6 @@
"node": ">=10.17.0"
}
},
"node_modules/hyphenate-style-name": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.1.0.tgz",
"integrity": "sha512-WDC/ui2VVRrz3jOVi+XtjqkDjiVjTtFaAGiW37k6b+ohyQ5wYDOGkvCZa8+H0nx3gyvv0+BST9xuOgIyGQ00gw=="
},
"node_modules/iconv-lite": {
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
@ -10665,11 +10577,6 @@
"url": "https://github.com/sponsors/wooorm"
}
},
"node_modules/is-in-browser": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/is-in-browser/-/is-in-browser-1.1.3.tgz",
"integrity": "sha512-FeXIBgG/CPGd/WUxuEyvgGTEfwiG9Z4EKGxjNMRqviiIIfsmgrpnHLffEDdwUHqNva1VEW91o3xBT/m8Elgl9g=="
},
"node_modules/is-map": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz",
@ -14299,93 +14206,6 @@
"node": ">=0.10.0"
}
},
"node_modules/jss": {
"version": "10.10.0",
"resolved": "https://registry.npmjs.org/jss/-/jss-10.10.0.tgz",
"integrity": "sha512-cqsOTS7jqPsPMjtKYDUpdFC0AbhYFLTcuGRqymgmdJIeQ8cH7+AgX7YSgQy79wXloZq2VvATYxUOUQEvS1V/Zw==",
"dependencies": {
"@babel/runtime": "^7.3.1",
"csstype": "^3.0.2",
"is-in-browser": "^1.1.3",
"tiny-warning": "^1.0.2"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/jss"
}
},
"node_modules/jss-plugin-camel-case": {
"version": "10.10.0",
"resolved": "https://registry.npmjs.org/jss-plugin-camel-case/-/jss-plugin-camel-case-10.10.0.tgz",
"integrity": "sha512-z+HETfj5IYgFxh1wJnUAU8jByI48ED+v0fuTuhKrPR+pRBYS2EDwbusU8aFOpCdYhtRc9zhN+PJ7iNE8pAWyPw==",
"dependencies": {
"@babel/runtime": "^7.3.1",
"hyphenate-style-name": "^1.0.3",
"jss": "10.10.0"
}
},
"node_modules/jss-plugin-default-unit": {
"version": "10.10.0",
"resolved": "https://registry.npmjs.org/jss-plugin-default-unit/-/jss-plugin-default-unit-10.10.0.tgz",
"integrity": "sha512-SvpajxIECi4JDUbGLefvNckmI+c2VWmP43qnEy/0eiwzRUsafg5DVSIWSzZe4d2vFX1u9nRDP46WCFV/PXVBGQ==",
"dependencies": {
"@babel/runtime": "^7.3.1",
"jss": "10.10.0"
}
},
"node_modules/jss-plugin-global": {
"version": "10.10.0",
"resolved": "https://registry.npmjs.org/jss-plugin-global/-/jss-plugin-global-10.10.0.tgz",
"integrity": "sha512-icXEYbMufiNuWfuazLeN+BNJO16Ge88OcXU5ZDC2vLqElmMybA31Wi7lZ3lf+vgufRocvPj8443irhYRgWxP+A==",
"dependencies": {
"@babel/runtime": "^7.3.1",
"jss": "10.10.0"
}
},
"node_modules/jss-plugin-nested": {
"version": "10.10.0",
"resolved": "https://registry.npmjs.org/jss-plugin-nested/-/jss-plugin-nested-10.10.0.tgz",
"integrity": "sha512-9R4JHxxGgiZhurDo3q7LdIiDEgtA1bTGzAbhSPyIOWb7ZubrjQe8acwhEQ6OEKydzpl8XHMtTnEwHXCARLYqYA==",
"dependencies": {
"@babel/runtime": "^7.3.1",
"jss": "10.10.0",
"tiny-warning": "^1.0.2"
}
},
"node_modules/jss-plugin-props-sort": {
"version": "10.10.0",
"resolved": "https://registry.npmjs.org/jss-plugin-props-sort/-/jss-plugin-props-sort-10.10.0.tgz",
"integrity": "sha512-5VNJvQJbnq/vRfje6uZLe/FyaOpzP/IH1LP+0fr88QamVrGJa0hpRRyAa0ea4U/3LcorJfBFVyC4yN2QC73lJg==",
"dependencies": {
"@babel/runtime": "^7.3.1",
"jss": "10.10.0"
}
},
"node_modules/jss-plugin-rule-value-function": {
"version": "10.10.0",
"resolved": "https://registry.npmjs.org/jss-plugin-rule-value-function/-/jss-plugin-rule-value-function-10.10.0.tgz",
"integrity": "sha512-uEFJFgaCtkXeIPgki8ICw3Y7VMkL9GEan6SqmT9tqpwM+/t+hxfMUdU4wQ0MtOiMNWhwnckBV0IebrKcZM9C0g==",
"dependencies": {
"@babel/runtime": "^7.3.1",
"jss": "10.10.0",
"tiny-warning": "^1.0.2"
}
},
"node_modules/jss-plugin-vendor-prefixer": {
"version": "10.10.0",
"resolved": "https://registry.npmjs.org/jss-plugin-vendor-prefixer/-/jss-plugin-vendor-prefixer-10.10.0.tgz",
"integrity": "sha512-UY/41WumgjW8r1qMCO8l1ARg7NHnfRVWRhZ2E2m0DMYsr2DD91qIXLyNhiX83hHswR7Wm4D+oDYNC1zWCJWtqg==",
"dependencies": {
"@babel/runtime": "^7.3.1",
"css-vendor": "^2.0.8",
"jss": "10.10.0"
}
},
"node_modules/jss/node_modules/csstype": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="
},
"node_modules/jsx-ast-utils": {
"version": "3.3.5",
"resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz",
@ -14563,9 +14383,22 @@
"loose-envify": "cli.js"
}
},
"node_modules/lottie-react": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/lottie-react/-/lottie-react-2.4.1.tgz",
"integrity": "sha512-LQrH7jlkigIIv++wIyrOYFLHSKQpEY4zehPicL9bQsrt1rnoKRYCYgpCUe5maqylNtacy58/sQDZTkwMcTRxZw==",
"dependencies": {
"lottie-web": "^5.10.2"
},
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
}
},
"node_modules/lottie-web": {
"version": "5.7.7",
"license": "MIT"
"version": "5.13.0",
"resolved": "https://registry.npmjs.org/lottie-web/-/lottie-web-5.13.0.tgz",
"integrity": "sha512-+gfBXl6sxXMPe8tKQm7qzLnUy5DUPJPKIyRHwtpCpyUEYjHYRJC/5gjUvdkuO2c3JllrPtHXH5UJJK8LRYl5yQ=="
},
"node_modules/lower-case": {
"version": "2.0.2",
@ -17299,20 +17132,6 @@
"version": "17.0.1",
"license": "MIT"
},
"node_modules/react-lottie": {
"version": "1.2.3",
"license": "MIT",
"dependencies": {
"babel-runtime": "^6.26.0",
"lottie-web": "^5.1.3"
},
"engines": {
"npm": "^3.0.0"
},
"peerDependencies": {
"react": "^0.14.7 || ^15.0.0 || ^16.0.0"
}
},
"node_modules/react-refresh": {
"version": "0.11.0",
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz",
@ -19235,6 +19054,19 @@
}
}
},
"node_modules/tailwindcss/node_modules/yaml": {
"version": "2.8.1",
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.1.tgz",
"integrity": "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==",
"optional": true,
"peer": true,
"bin": {
"yaml": "bin.mjs"
},
"engines": {
"node": ">= 14.6"
}
},
"node_modules/tapable": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz",
@ -19405,10 +19237,6 @@
"license": "MIT",
"optional": true
},
"node_modules/tiny-warning": {
"version": "1.0.3",
"license": "MIT"
},
"node_modules/tmpl": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz",
@ -19648,11 +19476,25 @@
"is-typedarray": "^1.0.0"
}
},
"node_modules/typescript": {
"version": "4.9.5",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz",
"integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==",
"peer": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
},
"engines": {
"node": ">=4.2.0"
}
},
"node_modules/typescript-plugin-styled-components": {
"version": "1.4.4",
"license": "MIT",
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/typescript-plugin-styled-components/-/typescript-plugin-styled-components-1.6.0.tgz",
"integrity": "sha512-cUCbOuY0iNr1JjPYC5MqCiABrfv2ouLift+7gi4vS0gBPQySUT006wHCjwzynMfU5LGMQaZqpssAgzRRjt30Ng==",
"peerDependencies": {
"typescript": "^2.5.2 || ^3.0"
"typescript": "^2.5.2 || ^3.0 || ^4.0"
}
},
"node_modules/unbox-primitive": {