chore(deps): upgrade all dependencies to latest stable

Bring backend and frontend to the latest stable releases (no pre-releases),
including major upgrades that required code migration. Both projects pass
typecheck and production builds.

Backend
- NestJS 10 -> 11 (common/core/platform-express/jwt/passport/bull/cli/
  schematics/testing), @nestjs/config 3->4, @nestjs/swagger 7->11,
  @nestjs/typeorm 10->11
- @kubernetes/client-node 0.21 -> 1.4: migrate ~200+ call sites across 6
  services to the v1 single-object argument API, unwrapped responses, err.code,
  setHeaderOptions for patch content-type, applyToHTTPSOptions. Add regression
  spec k8s-client-v1-migration.spec.ts.
- typeorm 0.3 -> 1.0: relations/select string arrays -> object form
- uuid 9->14 (drops @types/uuid), multer 1->2, bcrypt 5->6, helmet 7->8,
  class-validator 0.14->0.15
- TypeScript 5->6, ESLint 8->9, @typescript-eslint 6->8, jest 29->30,
  @types/node 20->24; tsconfig: strictPropertyInitialization:false,
  ignoreDeprecations, rootDir, explicit types[]
- @nestjs/config 4: jwt.strategy uses getOrThrow; @types/express kept at 4
  (Nest 11 runs Express 4)

Frontend
- React 18->19, Next 14->16 (async params via official codemod),
  Tailwind 3->4 (@tailwindcss/postcss, @import + @config, inline custom @apply),
  framer-motion 11->12, zustand 4->5, three 0.169->0.184, @react-three/* majors
- TypeScript 5->6 (tsconfig target es5->ES2017), ESLint 8->9,
  eslint-config-next 14->16

Infra/docs
- Dockerfiles node:20-alpine -> node:24-alpine (require-esm for k8s client)
- Add UPGRADE.md / UPGRADE.en.md; refresh README tech-stack versions

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
keyhan
2026-06-14 18:05:33 +03:30
parent 23386b73de
commit 8b77656bb7
31 changed files with 8974 additions and 7684 deletions
+3 -3
View File
@@ -1,12 +1,12 @@
# ---- Stage 1: Dependencies ----
FROM node:20-alpine AS deps
FROM node:24-alpine AS deps
WORKDIR /app
COPY package.json package-lock.json* ./
RUN npm ci
# ---- Stage 2: Build ----
FROM node:20-alpine AS builder
FROM node:24-alpine AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
@@ -18,7 +18,7 @@ ENV NEXT_TELEMETRY_DISABLED=1
RUN npm run build
# ---- Stage 3: Production ----
FROM node:20-alpine AS production
FROM node:24-alpine AS production
RUN apk add --no-cache dumb-init
+2 -1
View File
@@ -1,5 +1,6 @@
/// <reference types="next" />
/// <reference types="next/image-types/global" />
import "./.next/types/routes.d.ts";
// NOTE: This file should not be edited
// see https://nextjs.org/docs/basic-features/typescript for more information.
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
+2560 -2101
View File
File diff suppressed because it is too large Load Diff
+24 -24
View File
@@ -9,33 +9,33 @@
"lint": "next lint"
},
"dependencies": {
"@react-three/drei": "^9.122.0",
"@react-three/fiber": "^8.18.0",
"@react-three/postprocessing": "^2.19.1",
"@tanstack/react-query": "^5.17.0",
"axios": "^1.6.0",
"@react-three/drei": "^10.7.7",
"@react-three/fiber": "^9.6.1",
"@react-three/postprocessing": "^3.0.4",
"@tanstack/react-query": "^5.101.0",
"axios": "^1.17.0",
"clsx": "^2.1.0",
"framer-motion": "^11.18.2",
"framer-motion": "^12.40.0",
"lenis": "^1.3.23",
"lucide-react": "^1.7.0",
"next": "14.1.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-hook-form": "^7.49.0",
"react-toastify": "^11.0.5",
"three": "^0.169.0",
"zustand": "^4.5.0"
"lucide-react": "^1.18.0",
"next": "16.2.9",
"react": "^19.2.7",
"react-dom": "^19.2.7",
"react-hook-form": "^7.79.0",
"react-toastify": "^11.1.0",
"three": "^0.184.0",
"zustand": "^5.0.14"
},
"devDependencies": {
"@types/node": "^20.11.0",
"@types/react": "^18.2.0",
"@types/react-dom": "^18.2.0",
"@types/three": "^0.169.0",
"autoprefixer": "^10.4.17",
"eslint": "^8.56.0",
"eslint-config-next": "14.1.0",
"postcss": "^8.4.33",
"tailwindcss": "^3.4.1",
"typescript": "^5.3.3"
"@tailwindcss/postcss": "^4.3.1",
"@types/node": "^24.0.0",
"@types/react": "^19.2.17",
"@types/react-dom": "^19.2.3",
"@types/three": "^0.184.1",
"eslint": "^9.0.0",
"eslint-config-next": "16.2.9",
"postcss": "^8.5.15",
"tailwindcss": "^4.3.1",
"typescript": "^6.0.3"
}
}
+1 -2
View File
@@ -1,6 +1,5 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
'@tailwindcss/postcss': {},
},
};
+18 -12
View File
@@ -14,23 +14,29 @@ export function generateStaticParams() {
return locales.map((lang) => ({ lang }));
}
export async function generateMetadata({
params,
}: {
params: { lang: string };
}): Promise<Metadata> {
export async function generateMetadata(
props: {
params: Promise<{ lang: string }>;
}
): Promise<Metadata> {
const params = await props.params;
if (!isLocale(params.lang)) return {};
const dict = await getDictionary(params.lang);
return { title: dict.meta.title, description: dict.meta.description };
}
export default async function RootLayout({
children,
params,
}: {
children: React.ReactNode;
params: { lang: string };
}) {
export default async function RootLayout(
props: {
children: React.ReactNode;
params: Promise<{ lang: string }>;
}
) {
const params = await props.params;
const {
children
} = props;
if (!isLocale(params.lang)) notFound();
const locale: Locale = params.lang;
const dict = await getDictionary(locale);
+10 -10
View File
@@ -1,6 +1,5 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
@import 'tailwindcss';
@config '../../tailwind.config.ts';
@layer base {
body {
@@ -82,19 +81,20 @@
transition-shadow duration-200;
}
.card-hover {
@apply card hover:shadow-md hover:border-gray-300;
@apply bg-white rounded-2xl shadow-sm border border-gray-200/80 p-6
transition-shadow duration-200 hover:shadow-md hover:border-gray-300;
}
/* ─── Badges ──────────────────────────────────────────── */
.badge {
@apply inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-semibold;
}
.badge-green { @apply badge bg-emerald-100 text-emerald-700; }
.badge-red { @apply badge bg-red-100 text-red-700; }
.badge-blue { @apply badge bg-blue-100 text-blue-700; }
.badge-yellow { @apply badge bg-amber-100 text-amber-700; }
.badge-gray { @apply badge bg-gray-100 text-gray-600; }
.badge-purple { @apply badge bg-purple-100 text-purple-700; }
.badge-green { @apply inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-semibold bg-emerald-100 text-emerald-700; }
.badge-red { @apply inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-semibold bg-red-100 text-red-700; }
.badge-blue { @apply inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-semibold bg-blue-100 text-blue-700; }
.badge-yellow { @apply inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-semibold bg-amber-100 text-amber-700; }
.badge-gray { @apply inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-semibold bg-gray-100 text-gray-600; }
.badge-purple { @apply inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-semibold bg-purple-100 text-purple-700; }
/* ─── Stat card ──────────────────────────────────────── */
.stat-card {
+25 -7
View File
@@ -1,7 +1,11 @@
{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"target": "ES2017",
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
@@ -11,13 +15,27 @@
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"jsx": "react-jsx",
"incremental": true,
"plugins": [{ "name": "next" }],
"plugins": [
{
"name": "next"
}
],
"paths": {
"@/*": ["./src/*"]
"@/*": [
"./src/*"
]
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"]
"include": [
"next-env.d.ts",
"**/*.ts",
"**/*.tsx",
".next/types/**/*.ts",
".next/dev/types/**/*.ts"
],
"exclude": [
"node_modules"
]
}