diff --git a/frontend/.nvmrc b/frontend/.nvmrc index 92bc26a4217b..c4697fd5666f 100644 --- a/frontend/.nvmrc +++ b/frontend/.nvmrc @@ -1 +1 @@ -20.20.2 \ No newline at end of file +26.3.0 diff --git a/frontend/Dockerfile b/frontend/Dockerfile index 2ca4df0a33af..d66e1ab82f60 100644 --- a/frontend/Dockerfile +++ b/frontend/Dockerfile @@ -11,7 +11,7 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -FROM node:20.20.2-alpine AS base +FROM node:26.3.0-alpine AS base ENV PNPM_HOME="/pnpm" ENV PATH="$PNPM_HOME:$PATH" WORKDIR /app @@ -20,7 +20,7 @@ WORKDIR /app FROM base AS deps # Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed. -RUN apk add --no-cache libc6-compat && corepack enable && corepack prepare pnpm@8.9.0 --activate +RUN apk add --no-cache libc6-compat && npm install -g corepack && corepack enable && corepack prepare pnpm@8.9.0 --activate # Install dependencies based on the preferred package manager root workspace diff --git a/frontend/desktop/README.md b/frontend/desktop/README.md index b203efee9b0e..46e0e7169b81 100644 --- a/frontend/desktop/README.md +++ b/frontend/desktop/README.md @@ -139,7 +139,7 @@ Open [http://localhost:3000](http://localhost:3000) with your browser to see the "devDependencies": { "@types/js-yaml": "^4.0.5", "@types/lodash": "^4.14.194", - "@types/node": "18.15.11", + "@types/node": "^24", "@types/nprogress": "^0.2.0", "@types/react": "18.0.37", "@types/react-dom": "18.0.11" diff --git a/frontend/desktop/package.json b/frontend/desktop/package.json index d91e0056012d..e8ae8d466943 100644 --- a/frontend/desktop/package.json +++ b/frontend/desktop/package.json @@ -63,7 +63,6 @@ "immer": "^10.0.2", "js-cookie": "^3.0.5", "js-yaml": "^4.1.0", - "jsonwebtoken": "^9.0.2", "jwt-decode": "^4.0.0", "lodash": "^4.17.21", "lucide-react": "^0.488.0", @@ -97,26 +96,25 @@ "zustand": "^4.4.1" }, "devDependencies": { + "@playwright/test": "^1.58.1", "@testing-library/react": "^14.0.0", "@types/js-cookie": "^3.0.4", "@types/js-yaml": "^4.0.6", - "@types/jsonwebtoken": "^9.0.3", "@types/lodash": "^4.14.199", "@types/minio": "^7.1.1", - "@types/node": "18.15.11", + "@types/node": "^25.9.3", "@types/nodemailer": "^6.4.15", "@types/nprogress": "^0.2.1", "@types/react": "18.3.27", "@types/react-dom": "18.3.7", "@types/umami-browser": "^2.3.2", "@types/uuid": "^9.0.4", + "@vitejs/plugin-react": "^5.1.1", + "@vitest/browser": "4.0.15", + "@vitest/browser-playwright": "4.0.15", "dotenv-cli": "^7.3.0", "prettier": "^2.8.8", "vite-tsconfig-paths": "^5.1.4", - "vitest": "^4.0.12", - "@playwright/test": "^1.58.1", - "@vitest/browser": "4.0.15", - "@vitest/browser-playwright": "4.0.15", - "@vitejs/plugin-react": "^5.1.1" + "vitest": "^4.0.12" } } diff --git a/frontend/desktop/src/__tests__/unit/backend/auth/global-token.test.ts b/frontend/desktop/src/__tests__/unit/backend/auth/global-token.test.ts index efe9f0e5fd35..ac380bf5763f 100644 --- a/frontend/desktop/src/__tests__/unit/backend/auth/global-token.test.ts +++ b/frontend/desktop/src/__tests__/unit/backend/auth/global-token.test.ts @@ -5,7 +5,7 @@ import { generateOAuth2RefreshToken, verifyGlobalToken } from '@/services/backend/auth'; -import { sign } from 'jsonwebtoken'; +import { signJwt } from '@sealos/shared/server/jwt'; describe('verifyGlobalToken', () => { beforeEach(() => { @@ -31,7 +31,7 @@ describe('verifyGlobalToken', () => { } as any); it('maps oauth2 access token claims to legacy payload', async () => { - const token = generateOAuth2AccessToken({ + const token = await generateOAuth2AccessToken({ sub: 'oauth-uid', user_id: 'oauth-user-id', client_id: GLOBAL_TOKEN_CLIENT_ID @@ -46,7 +46,7 @@ describe('verifyGlobalToken', () => { }); it('rejects oauth2 refresh token', async () => { - const token = generateOAuth2RefreshToken({ + const token = await generateOAuth2RefreshToken({ sub: 'oauth-uid', user_id: 'oauth-user-id', client_id: GLOBAL_TOKEN_CLIENT_ID @@ -58,7 +58,7 @@ describe('verifyGlobalToken', () => { }); it('rejects oauth2 access token without subject', async () => { - const token = sign( + const token = await signJwt( { client_id: GLOBAL_TOKEN_CLIENT_ID, token_type: 'access_token', @@ -74,7 +74,7 @@ describe('verifyGlobalToken', () => { }); it('rejects oauth2 access token without user_id', async () => { - const token = sign( + const token = await signJwt( { sub: 'oauth-uid', client_id: GLOBAL_TOKEN_CLIENT_ID, diff --git a/frontend/desktop/src/pages/api/account/alerts/createAlert.ts b/frontend/desktop/src/pages/api/account/alerts/createAlert.ts index c3a1ad93c96a..4582536991e6 100644 --- a/frontend/desktop/src/pages/api/account/alerts/createAlert.ts +++ b/frontend/desktop/src/pages/api/account/alerts/createAlert.ts @@ -77,7 +77,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) return jsonRes(res, { code: 500, message: 'Billing service not configured' }); } - const billingToken = generateBillingToken({ + const billingToken = await generateBillingToken({ userUid: session.userUid, userId: session.userId }); diff --git a/frontend/desktop/src/pages/api/account/alerts/deleteAlerts.ts b/frontend/desktop/src/pages/api/account/alerts/deleteAlerts.ts index 344f6d24395e..0433870fb099 100644 --- a/frontend/desktop/src/pages/api/account/alerts/deleteAlerts.ts +++ b/frontend/desktop/src/pages/api/account/alerts/deleteAlerts.ts @@ -35,7 +35,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) return jsonRes(res, { code: 500, message: 'Billing service not configured' }); } - const billingToken = generateBillingToken({ + const billingToken = await generateBillingToken({ userUid: session.userUid, userId: session.userId }); diff --git a/frontend/desktop/src/pages/api/account/alerts/listAlerts.ts b/frontend/desktop/src/pages/api/account/alerts/listAlerts.ts index d1e3683efbad..eed55617558c 100644 --- a/frontend/desktop/src/pages/api/account/alerts/listAlerts.ts +++ b/frontend/desktop/src/pages/api/account/alerts/listAlerts.ts @@ -25,7 +25,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) return jsonRes(res, { code: 500, message: 'Billing service not configured' }); } - const billingToken = generateBillingToken({ + const billingToken = await generateBillingToken({ userUid: session.userUid, userId: session.userId }); diff --git a/frontend/desktop/src/pages/api/account/alerts/updateAlerts.ts b/frontend/desktop/src/pages/api/account/alerts/updateAlerts.ts index 19ed0aa2595e..70934ffa9915 100644 --- a/frontend/desktop/src/pages/api/account/alerts/updateAlerts.ts +++ b/frontend/desktop/src/pages/api/account/alerts/updateAlerts.ts @@ -42,7 +42,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) return jsonRes(res, { code: 500, message: 'Billing service not configured' }); } - const billingToken = generateBillingToken({ + const billingToken = await generateBillingToken({ userUid: session.userUid, userId: session.userId }); diff --git a/frontend/desktop/src/pages/api/account/enterpriseRealName.ts b/frontend/desktop/src/pages/api/account/enterpriseRealName.ts index c41c713e32c5..f3e33236507e 100644 --- a/frontend/desktop/src/pages/api/account/enterpriseRealName.ts +++ b/frontend/desktop/src/pages/api/account/enterpriseRealName.ts @@ -196,7 +196,7 @@ async function handlePost( const data = validationResult.data; - const globalToken = generateLegacyGlobalToken({ + const globalToken = await generateLegacyGlobalToken({ userUid: payload.userUid, userId: payload.userId, regionUid: payload.regionUid @@ -346,7 +346,7 @@ async function handleGetBanks( const config: UnionPay3060Config = realNameAuthProvider.config as UnionPay3060Config; const enterpriseRealNameAuthApi = config.api; - const globalToken = generateLegacyGlobalToken({ + const globalToken = await generateLegacyGlobalToken({ userUid: payload.userUid, userId: payload.userId, regionUid: payload.regionUid diff --git a/frontend/desktop/src/pages/api/account/generateRealNameQRcodeUri.ts b/frontend/desktop/src/pages/api/account/generateRealNameQRcodeUri.ts index 2feb8b5da02c..db1b05b97365 100644 --- a/frontend/desktop/src/pages/api/account/generateRealNameQRcodeUri.ts +++ b/frontend/desktop/src/pages/api/account/generateRealNameQRcodeUri.ts @@ -123,7 +123,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) global.AppConfig?.common.realNameCallbackUrl || `https://${global.AppConfig?.cloud.domain}/api/account/faceIdRealNameAuthCallback`; - const globalToken = generateLegacyGlobalToken( + const globalToken = await generateLegacyGlobalToken( { userUid: payload.userUid, userId: payload.userId diff --git a/frontend/desktop/src/pages/api/account/refreshRealNameQRecodeUri.ts b/frontend/desktop/src/pages/api/account/refreshRealNameQRecodeUri.ts index 80174f53d69d..fa35cc2df57c 100644 --- a/frontend/desktop/src/pages/api/account/refreshRealNameQRecodeUri.ts +++ b/frontend/desktop/src/pages/api/account/refreshRealNameQRecodeUri.ts @@ -121,7 +121,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) global.AppConfig?.common.realNameCallbackUrl || `https://${global.AppConfig?.cloud.domain}/api/account/faceIdRealNameAuthCallback`; - const globalToken = generateLegacyGlobalToken( + const globalToken = await generateLegacyGlobalToken( { userUid: payload.userUid, userId: payload.userId diff --git a/frontend/desktop/src/pages/api/auth/globalToken.ts b/frontend/desktop/src/pages/api/auth/globalToken.ts index b77c8b4cbe42..12dd9eecb7d5 100644 --- a/frontend/desktop/src/pages/api/auth/globalToken.ts +++ b/frontend/desktop/src/pages/api/auth/globalToken.ts @@ -15,7 +15,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) code: 200, message: 'Successfully', data: { - token: generateGlobalAccessToken({ + token: await generateGlobalAccessToken({ sub: regionUserData.userUid, user_id: regionUserData.userId }) diff --git a/frontend/desktop/src/pages/api/auth/namespace/switch.ts b/frontend/desktop/src/pages/api/auth/namespace/switch.ts index 84576aa15898..c0e708820b78 100644 --- a/frontend/desktop/src/pages/api/auth/namespace/switch.ts +++ b/frontend/desktop/src/pages/api/auth/namespace/switch.ts @@ -40,8 +40,8 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) userId: payload.userId, userUid: payload.userUid }; - const token = generateRegionalToken(jwtPayload); - const appToken = generateAppToken(jwtPayload); + const token = await generateRegionalToken(jwtPayload); + const appToken = await generateAppToken(jwtPayload); const data = { token, appToken diff --git a/frontend/desktop/src/pages/api/desktop/getBilling.ts b/frontend/desktop/src/pages/api/desktop/getBilling.ts index 73a59e5dbea6..d8c9b731abac 100644 --- a/frontend/desktop/src/pages/api/desktop/getBilling.ts +++ b/frontend/desktop/src/pages/api/desktop/getBilling.ts @@ -31,7 +31,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) const base = global.AppConfig.desktop.auth.billingUrl as string; const consumptionUrl = base + '/account/v1alpha1/costs/all-region-consumption'; - const billingToken = generateBillingToken({ + const billingToken = await generateBillingToken({ userUid: payload.userUid, userId: payload.userId }); diff --git a/frontend/desktop/src/pages/api/plan/info.ts b/frontend/desktop/src/pages/api/plan/info.ts index ef4c053cf60d..a70693655aca 100644 --- a/frontend/desktop/src/pages/api/plan/info.ts +++ b/frontend/desktop/src/pages/api/plan/info.ts @@ -25,7 +25,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) return jsonRes(res, { code: 500, message: 'Billing service not configured' }); } - const billingToken = generateBillingToken({ + const billingToken = await generateBillingToken({ userUid: payload.userUid, userId: payload.userId }); diff --git a/frontend/desktop/src/pages/api/plan/workspaces-plans.ts b/frontend/desktop/src/pages/api/plan/workspaces-plans.ts index a5e61b93c382..a9d602c41be6 100644 --- a/frontend/desktop/src/pages/api/plan/workspaces-plans.ts +++ b/frontend/desktop/src/pages/api/plan/workspaces-plans.ts @@ -24,7 +24,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) return jsonRes(res, { code: 500, message: 'Billing service not configured' }); } - const billingToken = generateBillingToken({ + const billingToken = await generateBillingToken({ userUid: payload.userUid, userId: payload.userId }); diff --git a/frontend/desktop/src/pages/api/workspace/getQuota.ts b/frontend/desktop/src/pages/api/workspace/getQuota.ts index 9e3c2a19d1ca..5a81ace27125 100644 --- a/frontend/desktop/src/pages/api/workspace/getQuota.ts +++ b/frontend/desktop/src/pages/api/workspace/getQuota.ts @@ -31,7 +31,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) return jsonRes(res, { code: 500, message: 'Billing service not configured' }); } - const billingToken = generateBillingToken({ + const billingToken = await generateBillingToken({ userUid: session.userUid, userId: session.userId }); diff --git a/frontend/desktop/src/services/backend/auth.ts b/frontend/desktop/src/services/backend/auth.ts index 46e5c36dfc0a..66f06ed55ed5 100644 --- a/frontend/desktop/src/services/backend/auth.ts +++ b/frontend/desktop/src/services/backend/auth.ts @@ -9,8 +9,8 @@ import { OAuth2RefreshTokenPayload, OnceTokenPayload } from '@/types/token'; +import { signJwt, verifyJwt as verifySharedJwt } from '@sealos/shared/server/jwt'; import { IncomingHttpHeaders } from 'http'; -import { sign, verify } from 'jsonwebtoken'; const regionUID = () => global.AppConfig?.cloud.regionUID || '123456789'; export const globalJwtSecret = () => global.AppConfig?.desktop.auth.jwt.global || '123456789'; @@ -129,19 +129,11 @@ export const verifyGlobalToken = async ( return null; }; +const signPayload = (payload: T, secret: string, expiresIn: string) => + signJwt(payload as Record, secret, { expiresIn }); + const verifyJwt = (token: string | undefined, secret: string) => - new Promise((resolve) => { - if (!token) return resolve(null); - verify(token, secret, (err, payload) => { - if (err) { - resolve(null); - } else if (!payload) { - resolve(null); - } else { - resolve(payload as T); - } - }); - }); + verifySharedJwt(token, secret) as Promise; export const verifyRegionalJwt = (token?: string) => verifyJwt(token, regionalJwtSecret()); @@ -160,13 +152,13 @@ export const verifyAppToken = async (header: IncomingHttpHeaders) => { }; export const generateBillingToken = (props: BillingTokenPayload) => - sign(props, internalJwtSecret(), { expiresIn: '3600000' }); + signPayload(props, internalJwtSecret(), '3600000'); export const generateRegionalToken = (props: AccessTokenPayload) => - sign(props, regionalJwtSecret(), { expiresIn: '7d' }); + signPayload(props, regionalJwtSecret(), '7d'); export const generateAppToken = (props: AccessTokenPayload) => - sign(props, internalJwtSecret(), { expiresIn: '7d' }); + signPayload(props, internalJwtSecret(), '7d'); /** * Signs global token. @@ -182,7 +174,7 @@ export const signGlobalToken = (props: GlobalJwtClaims, expiresIn?: string) => { ...props }; - return sign(payload, globalJwtSecret(), { expiresIn: expiresIn ?? '7d' }); + return signPayload(payload, globalJwtSecret(), expiresIn ?? '7d'); }; /** @@ -199,7 +191,7 @@ export const generateLegacyGlobalToken = (props: GlobalTokenPayload, expiresIn?: // [FIXME] Should have a client_id in here but external services will reject the additional claim. }; - return sign(payload, globalJwtSecret(), { expiresIn: expiresIn ?? '7d' }); + return signPayload(payload, globalJwtSecret(), expiresIn ?? '7d'); }; /** @@ -267,10 +259,10 @@ export const verifyOAuth2RefreshToken = (token?: string) => verifyOAuth2TokenByType(token, REFRESH_TOKEN_TYPE); export const generateOnceToken = (props: OnceTokenPayload) => - sign(props, regionalJwtSecret(), { expiresIn: '1800000' }); + signPayload(props, regionalJwtSecret(), '1800000'); export const generateCronJobToken = (props: CronJobTokenPayload) => - sign(props, internalJwtSecret(), { expiresIn: '60000' }); + signPayload(props, internalJwtSecret(), '60000'); export const callBillingService = async ( endpoint: string, @@ -282,7 +274,7 @@ export const callBillingService = async ( throw new Error('Billing service not configured'); } - const billingToken = generateBillingToken(payload); + const billingToken = await generateBillingToken(payload); const regionDomain = body.regionDomain || global.AppConfig.cloud.domain; const requestBody = { ...body, diff --git a/frontend/desktop/src/services/backend/cronjob/mergeUserCr.ts b/frontend/desktop/src/services/backend/cronjob/mergeUserCr.ts index 1dd7b4561a4b..a1a9500ac582 100644 --- a/frontend/desktop/src/services/backend/cronjob/mergeUserCr.ts +++ b/frontend/desktop/src/services/backend/cronjob/mergeUserCr.ts @@ -264,21 +264,21 @@ export class MergeUserCrJob implements CronJobStatus { headers: { Authorization: 'Bearer ' + - generateBillingToken({ + (await generateBillingToken({ userUid: mergeUser.uid, userId: mergeUser.id - }) + })) } } ), axios.post(workorderUrl, { - token: generateCronJobToken({ + token: await generateCronJobToken({ userUid: user.id, mergeUserUid: mergeUser.id }) }), axios.post(cvmUrl, { - token: generateCronJobToken({ + token: await generateCronJobToken({ userUid: user.uid, mergeUserUid: mergeUser.uid }) diff --git a/frontend/desktop/src/services/backend/globalAuth.ts b/frontend/desktop/src/services/backend/globalAuth.ts index e9f78553a6c7..ecd59f67d8cf 100644 --- a/frontend/desktop/src/services/backend/globalAuth.ts +++ b/frontend/desktop/src/services/backend/globalAuth.ts @@ -498,7 +498,7 @@ export const getGlobalToken = async ({ // user is deleted or banned if (user.status !== UserStatus.NORMAL_USER) return null; - const token = generateGlobalAccessToken({ + const token = await generateGlobalAccessToken({ sub: user.uid, user_id: user.id, preferred_username: user.nickname diff --git a/frontend/desktop/src/services/backend/middleware/checkResource.ts b/frontend/desktop/src/services/backend/middleware/checkResource.ts index 08ac3f39d66e..8518c054ff62 100644 --- a/frontend/desktop/src/services/backend/middleware/checkResource.ts +++ b/frontend/desktop/src/services/backend/middleware/checkResource.ts @@ -126,7 +126,7 @@ export const otherRegionResourceGuard = (region) => region.uid !== global.AppConfig.cloud.regionUID ); const otherCheckResp = await Promise.all( - regionTarget.map((region) => + regionTarget.map(async (region) => fetch( process.env.NODE_ENV === 'development' ? `http://127.0.0.1:3000/api/auth/delete/checkResource` @@ -134,7 +134,7 @@ export const otherRegionResourceGuard = { headers: { authorization: encodeURI( - generateLegacyGlobalToken({ + await generateLegacyGlobalToken({ userUid: userUid, userId: userId }) diff --git a/frontend/desktop/src/services/backend/middleware/oauth.ts b/frontend/desktop/src/services/backend/middleware/oauth.ts index ab1bc40ad372..3fae6b94f463 100644 --- a/frontend/desktop/src/services/backend/middleware/oauth.ts +++ b/frontend/desktop/src/services/backend/middleware/oauth.ts @@ -2,7 +2,6 @@ import { NextApiRequest, NextApiResponse } from 'next'; import { jsonRes } from '../response'; import { isNumber } from 'lodash'; import { TgithubToken, TgithubUser, TWechatToken, TWechatUser } from '@/types/user'; -import * as jwt from 'jsonwebtoken'; import { userCanMerge } from '@/utils/tools'; import { ProviderType } from 'prisma/global/generated/client'; import { globalPrisma } from '../db/init'; @@ -13,6 +12,7 @@ import { UNBIND_STATUS } from '@/types/response/unbind'; import { SemData } from '@/types/sem'; import axios from 'axios'; import { AdClickData } from '@/types/adClick'; +import { decodeJwt } from '@sealos/shared/server/jwt'; export const OauthCodeFilter = async ( req: NextApiRequest, @@ -95,7 +95,7 @@ export const googleOAuthGuard = token_type: string; id_token: string; }; - const userInfo = jwt.decode(__data.id_token) as { + const userInfo = decodeJwt<{ iss: string; azp: string; aud: string; @@ -109,7 +109,8 @@ export const googleOAuthGuard = locale: string; iat: number; exp: number; - }; + }>(__data.id_token); + if (!userInfo) throw Error('get userInfo error'); const name = userInfo.name; const id = userInfo.sub; const avatar_url = userInfo.picture; diff --git a/frontend/desktop/src/services/backend/oauth2/service.ts b/frontend/desktop/src/services/backend/oauth2/service.ts index 70aa65785bd2..a2da9c6b3e25 100644 --- a/frontend/desktop/src/services/backend/oauth2/service.ts +++ b/frontend/desktop/src/services/backend/oauth2/service.ts @@ -187,7 +187,7 @@ export const exchangeDeviceCodeForToken = async ( throw new OAuth2HttpError(400, 'invalid_grant'); } - const accessToken = generateOAuth2AccessToken( + const accessToken = await generateOAuth2AccessToken( { sub: user.uid, user_id: user.id, @@ -196,7 +196,7 @@ export const exchangeDeviceCodeForToken = async ( }, `${ACCESS_TOKEN_EXPIRES_IN_SECONDS}s` ); - const refreshToken = generateOAuth2RefreshToken( + const refreshToken = await generateOAuth2RefreshToken( { sub: user.uid, user_id: user.id, @@ -248,7 +248,7 @@ export const exchangeRefreshTokenForToken = async ( throw new OAuth2HttpError(400, 'invalid_grant', 'Refresh token does not match client'); } - const accessToken = generateOAuth2AccessToken( + const accessToken = await generateOAuth2AccessToken( { sub: refreshPayload.sub, user_id: refreshPayload.user_id, @@ -258,7 +258,7 @@ export const exchangeRefreshTokenForToken = async ( }, `${ACCESS_TOKEN_EXPIRES_IN_SECONDS}s` ); - const refreshToken = generateOAuth2RefreshToken( + const refreshToken = await generateOAuth2RefreshToken( { sub: refreshPayload.sub, user_id: refreshPayload.user_id, diff --git a/frontend/desktop/src/services/backend/regionAuth.ts b/frontend/desktop/src/services/backend/regionAuth.ts index 02ac1639d3f9..c6edd75c0543 100644 --- a/frontend/desktop/src/services/backend/regionAuth.ts +++ b/frontend/desktop/src/services/backend/regionAuth.ts @@ -347,8 +347,8 @@ export async function getRegionToken({ const { kubeconfig, payload } = result; return { kubeconfig, - token: generateRegionalToken(payload), - appToken: generateAppToken(payload) + token: await generateRegionalToken(payload), + appToken: await generateAppToken(payload) }; } @@ -573,8 +573,8 @@ export async function initRegionToken({ const { kubeconfig, payload } = result; return { kubeconfig, - token: generateRegionalToken(payload), - appToken: generateAppToken(payload) + token: await generateRegionalToken(payload), + appToken: await generateAppToken(payload) }; } diff --git a/frontend/desktop/src/services/backend/svc/checkResource.ts b/frontend/desktop/src/services/backend/svc/checkResource.ts index f0c9290ad6e2..191d1104e4ff 100644 --- a/frontend/desktop/src/services/backend/svc/checkResource.ts +++ b/frontend/desktop/src/services/backend/svc/checkResource.ts @@ -48,7 +48,7 @@ const checkResourceResponse = async (userId: string, userUid: string, userCrName const authorization = 'Bearer ' + encodeURI( - generateBillingToken({ + await generateBillingToken({ userUid, userId }) @@ -157,7 +157,7 @@ export const allRegionResourceSvc = resource: currentResourceCheck }); const otherCheckResp = await Promise.all( - regionTarget.map((region) => + regionTarget.map(async (region) => fetch( process.env.NODE_ENV === 'development' ? `http://127.0.0.1:3000/api/auth/delete/checkCurrentResource` @@ -165,7 +165,7 @@ export const allRegionResourceSvc = { headers: { authorization: encodeURI( - generateLegacyGlobalToken({ + await generateLegacyGlobalToken({ userUid: userUid, userId: userId }) diff --git a/frontend/package.json b/frontend/package.json index deab4d137bdc..9b7dc4d186ad 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -51,7 +51,7 @@ }, "packageManager": "pnpm@8.9.0", "engines": { - "node": "20.20.2", + "node": "26.3.0", "pnpm": "8.9.0" } } diff --git a/frontend/packages/client-sdk/package.json b/frontend/packages/client-sdk/package.json index 5e3e53652661..a23d04978612 100644 --- a/frontend/packages/client-sdk/package.json +++ b/frontend/packages/client-sdk/package.json @@ -57,7 +57,7 @@ "@rollup/plugin-typescript": "^11.1.4", "@types/js-cookie": "^3.0.4", "@types/js-yaml": "^4.0.6", - "@types/node": "^20.7.1", + "@types/node": "^25.9.3", "@types/uuid": "^9.0.4", "rollup": "2.79.1", "rollup-plugin-copy": "^3.5.0", @@ -74,4 +74,4 @@ "peerDependencies": { "@kubernetes/client-node": "^0.18.1" } -} \ No newline at end of file +} diff --git a/frontend/packages/gtm/package.json b/frontend/packages/gtm/package.json index a74c9e37a7b3..8cfda9b77ca6 100644 --- a/frontend/packages/gtm/package.json +++ b/frontend/packages/gtm/package.json @@ -50,7 +50,7 @@ }, "devDependencies": { "@types/react": "18.3.27", - "@types/node": "18.15.11", + "@types/node": "^25.9.3", "typescript": "^5.0.0" } } diff --git a/frontend/packages/mcp-sdk/package.json b/frontend/packages/mcp-sdk/package.json index cc8d01810d85..e2953e9e6aa7 100644 --- a/frontend/packages/mcp-sdk/package.json +++ b/frontend/packages/mcp-sdk/package.json @@ -33,7 +33,7 @@ "license": "ISC", "devDependencies": { "@rollup/plugin-typescript": "^11.1.4", - "@types/node": "^20.7.1", + "@types/node": "^25.9.3", "rollup": "2.79.1", "rollup-plugin-copy": "^3.5.0", "rollup-plugin-dts": "^4.2.3", diff --git a/frontend/packages/shadcn-ui/package.json b/frontend/packages/shadcn-ui/package.json index 51ef80461164..0d6261322080 100644 --- a/frontend/packages/shadcn-ui/package.json +++ b/frontend/packages/shadcn-ui/package.json @@ -35,7 +35,7 @@ "tw-animate-css": "^1.3.4" }, "devDependencies": { - "@types/node": "^20.7.1", + "@types/node": "^25.9.3", "@types/react": "18.3.27", "@types/react-dom": "18.3.7", "@vitejs/plugin-react": "^5.1.1", diff --git a/frontend/packages/shared/__tests__/server/jwt.test.ts b/frontend/packages/shared/__tests__/server/jwt.test.ts new file mode 100644 index 000000000000..78cdc9df4202 --- /dev/null +++ b/frontend/packages/shared/__tests__/server/jwt.test.ts @@ -0,0 +1,76 @@ +import { describe, expect, it } from 'vitest'; +import { decodeJwt, isJwtToken, signJwt, verifyJwt, verifyJwtOrThrow } from '../../src-server/jwt'; + +type TestPayload = { + sub: string; + user_id: string; +}; + +describe('server jwt utilities', () => { + it('signs and verifies HS256 tokens', async () => { + const token = await signJwt( + { + sub: 'user-uid', + user_id: 'user-id' + }, + 'test-secret', + { expiresIn: '1h' } + ); + + const payload = await verifyJwt(token, 'test-secret'); + + expect(payload).toMatchObject({ + sub: 'user-uid', + user_id: 'user-id' + }); + expect(payload?.iat).toEqual(expect.any(Number)); + expect(payload?.exp).toEqual(expect.any(Number)); + }); + + it('returns null when the secret is wrong', async () => { + const token = await signJwt({ sub: 'user-uid' }, 'test-secret', { expiresIn: '1h' }); + + const payload = await verifyJwt(token, 'wrong-secret'); + + expect(payload).toBeNull(); + }); + + it('returns null when the token is expired', async () => { + const token = await signJwt({ sub: 'user-uid' }, 'test-secret', { expiresIn: '-1s' }); + + const payload = await verifyJwt(token, 'test-secret'); + + expect(payload).toBeNull(); + }); + + it('throws when verifyJwtOrThrow receives an invalid token', async () => { + await expect(verifyJwtOrThrow('invalid-token', 'test-secret')).rejects.toThrow(); + }); + + it('decodes payload without verifying the signature', async () => { + const token = await signJwt( + { + sub: 'user-uid', + role: 'admin' + }, + 'test-secret', + { expiresIn: '60000' } + ); + + const payload = decodeJwt<{ sub: string; role: string }>(token); + + expect(payload).toMatchObject({ + sub: 'user-uid', + role: 'admin' + }); + }); + + it('returns null when decoding malformed jwt', () => { + expect(decodeJwt('not-a-jwt')).toBeNull(); + }); + + it('detects compact jwt shape', () => { + expect(isJwtToken('a.b.c')).toBe(true); + expect(isJwtToken('a.b')).toBe(false); + }); +}); diff --git a/frontend/packages/shared/package.json b/frontend/packages/shared/package.json index 53b248fcaaaa..ffd662d4cd0b 100644 --- a/frontend/packages/shared/package.json +++ b/frontend/packages/shared/package.json @@ -12,6 +12,7 @@ "./shadcn": "./src/components/shadcn/index.ts", "./components/shadcn/styles.css": "./src/components/shadcn/styles.css", "./server/config": "./src-server/config/index.ts", + "./server/jwt": "./src-server/jwt/index.ts", "./server/v2alpha": "./src-server/v2alpha/error.ts" }, "typesVersions": { @@ -25,6 +26,9 @@ "server/config": [ "src-server/config/index.ts" ], + "server/jwt": [ + "src-server/jwt/index.ts" + ], "server/v2alpha": [ "src-server/v2alpha/error.ts" ] @@ -51,6 +55,9 @@ "zod": "^3 || ^4", "zustand": "^4 || ^5" }, + "dependencies": { + "jose": "^6.0.10" + }, "peerDependenciesMeta": { "@chakra-ui/icons": { "optional": true, @@ -102,7 +109,7 @@ "@chakra-ui/react": "^2.8.1", "@tanstack/react-query": "^4.36.1", "@types/js-yaml": "^4.0.9", - "@types/node": "^20.7.1", + "@types/node": "^25.9.3", "@types/react": "18.3.27", "@types/react-dom": "18.3.7", "@vitejs/plugin-react": "^5.1.1", diff --git a/frontend/packages/shared/src-server/jwt/index.ts b/frontend/packages/shared/src-server/jwt/index.ts new file mode 100644 index 000000000000..79f13bd6ab4f --- /dev/null +++ b/frontend/packages/shared/src-server/jwt/index.ts @@ -0,0 +1,74 @@ +import { decodeJwt as joseDecodeJwt, jwtVerify, SignJWT, type JWTPayload } from 'jose'; + +const textEncoder = new TextEncoder(); + +export type JwtPayload = JWTPayload; +export type JwtExpiresIn = number | string | Date; + +export type SignJwtOptions = { + expiresIn?: JwtExpiresIn; +}; + +type SignableJwtPayload = JWTPayload & Record; + +const getSecretKey = (secret: string) => textEncoder.encode(secret); + +const isNumericString = (value: string) => /^-?\d+$/.test(value); + +const toExpirationTime = (expiresIn: JwtExpiresIn) => { + if (typeof expiresIn === 'string' && isNumericString(expiresIn)) { + return new Date(Date.now() + Number(expiresIn)); + } + + return expiresIn; +}; + +export const isJwtToken = (token: string): boolean => token.split('.').length === 3; + +export const signJwt = ( + payload: T, + secret: string, + options: SignJwtOptions = {} +): Promise => { + let jwt = new SignJWT(payload as SignableJwtPayload) + .setProtectedHeader({ alg: 'HS256', typ: 'JWT' }) + .setIssuedAt(); + + if (options.expiresIn !== undefined) { + jwt = jwt.setExpirationTime(toExpirationTime(options.expiresIn)); + } + + return jwt.sign(getSecretKey(secret)); +}; + +export const verifyJwtOrThrow = async ( + token: string, + secret: string +): Promise => { + const { payload } = await jwtVerify(token, getSecretKey(secret), { + algorithms: ['HS256'] + }); + + return payload; +}; + +export const verifyJwt = async ( + token: string | undefined, + secret: string +): Promise<(T & JWTPayload) | null> => { + if (!token) return null; + + try { + return await verifyJwtOrThrow(token, secret); + } catch { + return null; + } +}; + +export const decodeJwt = (token: string): (T & JWTPayload) | null => { + try { + return joseDecodeJwt(token); + } catch { + return null; + } +}; diff --git a/frontend/packages/shared/vitest.config.mts b/frontend/packages/shared/vitest.config.mts index feef537783e2..dc9a41ec46ed 100644 --- a/frontend/packages/shared/vitest.config.mts +++ b/frontend/packages/shared/vitest.config.mts @@ -6,6 +6,17 @@ export default defineConfig({ plugins: [react()], test: { projects: [ + { + extends: true, + test: { + name: 'server', + globals: true, + environment: 'node', + passWithNoTests: true, + include: ['__tests__/server/**/*.{test,spec}.ts'], + exclude: ['node_modules', 'dist'] + } + }, { extends: true, test: { diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index d950c14dec2b..da7d9dc0e907 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -146,9 +146,6 @@ importers: js-yaml: specifier: ^4.1.0 version: 4.1.0 - jsonwebtoken: - specifier: ^9.0.2 - version: 9.0.2 jwt-decode: specifier: ^4.0.0 version: 4.0.0 @@ -255,9 +252,6 @@ importers: '@types/js-yaml': specifier: ^4.0.6 version: 4.0.9 - '@types/jsonwebtoken': - specifier: ^9.0.3 - version: 9.0.5 '@types/lodash': specifier: ^4.14.199 version: 4.14.202 @@ -265,8 +259,8 @@ importers: specifier: ^7.1.1 version: 7.1.1 '@types/node': - specifier: 18.15.11 - version: 18.15.11 + specifier: ^25.9.3 + version: 25.9.3 '@types/nodemailer': specifier: ^6.4.15 version: 6.4.15 @@ -305,7 +299,7 @@ importers: version: 5.1.4(typescript@5.9.3)(vite@7.2.6) vitest: specifier: ^4.0.12 - version: 4.0.15(@types/node@18.15.11)(@vitest/browser-playwright@4.0.15)(sass@1.69.5) + version: 4.0.15(@types/node@25.9.3)(@vitest/browser-playwright@4.0.15)(@vitest/ui@4.0.12)(jsdom@27.2.0)(sass@1.69.5) packages/client-sdk: dependencies: @@ -338,8 +332,8 @@ importers: specifier: ^4.0.6 version: 4.0.9 '@types/node': - specifier: ^20.7.1 - version: 20.10.0 + specifier: ^25.9.3 + version: 25.9.3 '@types/uuid': specifier: ^9.0.4 version: 9.0.7 @@ -385,8 +379,8 @@ importers: version: 18.3.1(react@18.3.1) devDependencies: '@types/node': - specifier: 18.15.11 - version: 18.15.11 + specifier: ^25.9.3 + version: 25.9.3 '@types/react': specifier: 18.3.27 version: 18.3.27 @@ -422,8 +416,8 @@ importers: specifier: ^11.1.4 version: 11.1.5(rollup@2.79.1)(tslib@2.6.2)(typescript@5.2.2) '@types/node': - specifier: ^20.7.1 - version: 20.17.32 + specifier: ^25.9.3 + version: 25.9.3 rollup: specifier: 2.79.1 version: 2.79.1 @@ -525,8 +519,8 @@ importers: version: 1.3.4 devDependencies: '@types/node': - specifier: ^20.7.1 - version: 20.17.32 + specifier: ^25.9.3 + version: 25.9.3 '@types/react': specifier: 18.3.27 version: 18.3.27 @@ -553,9 +547,13 @@ importers: version: 18.3.1(react@18.3.1) vitest: specifier: ^4.0.12 - version: 4.0.15(@types/node@20.17.32)(@vitest/browser-playwright@4.0.15) + version: 4.0.15(@types/node@25.9.3)(@vitest/browser-playwright@4.0.15) packages/shared: + dependencies: + jose: + specifier: ^6.0.10 + version: 6.0.10 devDependencies: '@chakra-ui/icons': specifier: ^2.1.1 @@ -570,8 +568,8 @@ importers: specifier: ^4.0.9 version: 4.0.9 '@types/node': - specifier: ^20.7.1 - version: 20.17.32 + specifier: ^25.9.3 + version: 25.9.3 '@types/react': specifier: 18.3.27 version: 18.3.27 @@ -631,7 +629,7 @@ importers: version: 5.9.3 vitest: specifier: ^4.0.12 - version: 4.0.15(@types/node@20.17.32)(@vitest/browser-playwright@4.0.15) + version: 4.0.15(@types/node@25.9.3)(@vitest/browser-playwright@4.0.15) zod: specifier: ^4.3.5 version: 4.3.5 @@ -772,8 +770,8 @@ importers: specifier: ^9.0.8 version: 9.0.8(react@18.3.1) echarts: - specifier: ^5.4.3 - version: 5.4.3 + specifier: ^5.6.0 + version: 5.6.0 framer-motion: specifier: ^10.16.4 version: 10.16.5(react-dom@18.3.1)(react@18.3.1) @@ -792,9 +790,6 @@ importers: immer: specifier: ^10.1.1 version: 10.1.1 - jsonwebtoken: - specifier: ^9.0.2 - version: 9.0.2 lucide-react: specifier: ^0.555.0 version: 0.555.0(react@18.3.1) @@ -853,12 +848,9 @@ importers: '@playwright/test': specifier: ^1.58.1 version: 1.59.1 - '@types/jsonwebtoken': - specifier: ^9.0.3 - version: 9.0.5 '@types/node': - specifier: ^20 - version: 20.10.0 + specifier: ^25.9.3 + version: 25.9.3 '@types/react': specifier: 18.3.27 version: 18.3.27 @@ -906,7 +898,7 @@ importers: version: 5.1.4(typescript@5.2.2)(vite@7.2.6) vitest: specifier: ^4.0.12 - version: 4.0.15(@types/node@20.10.0)(@vitest/browser-playwright@4.0.15) + version: 4.0.15(@types/node@25.9.3)(@vitest/browser-playwright@4.0.15) providers/applaunchpad: dependencies: @@ -958,9 +950,6 @@ importers: '@tanstack/react-table': specifier: ^8.10.7 version: 8.10.7(react-dom@18.3.1)(react@18.3.1) - '@types/jsonwebtoken': - specifier: ^9.0.3 - version: 9.0.5 ansi_up: specifier: ^5.2.1 version: 5.2.1 @@ -986,8 +975,8 @@ importers: specifier: ^5.6.1 version: 5.6.1 echarts: - specifier: ^5.4.3 - version: 5.4.3 + specifier: ^5.6.0 + version: 5.6.0 fast-json-patch: specifier: ^3.1.1 version: 3.1.1 @@ -1012,9 +1001,6 @@ importers: js-yaml: specifier: ^4.1.0 version: 4.1.0 - jsonwebtoken: - specifier: ^9.0.2 - version: 9.0.2 jszip: specifier: ^3.10.1 version: 3.10.1 @@ -1113,8 +1099,8 @@ importers: specifier: ^4.14.199 version: 4.14.202 '@types/node': - specifier: 18.13.0 - version: 18.13.0 + specifier: ^25.9.3 + version: 25.9.3 '@types/nprogress': specifier: ^0.2.1 version: 0.2.3 @@ -1162,7 +1148,7 @@ importers: version: 5.1.4(typescript@5.2.2)(vite@7.2.6) vitest: specifier: ^4.0.12 - version: 4.0.15(@types/node@18.13.0)(@vitest/browser-playwright@4.0.15)(@vitest/ui@4.0.12)(jsdom@27.2.0)(sass@1.69.5) + version: 4.0.15(@types/node@25.9.3)(@vitest/browser-playwright@4.0.15)(@vitest/ui@4.0.12)(jsdom@27.2.0)(sass@1.69.5) webpack-bundle-analyzer: specifier: ^4.9.1 version: 4.10.1 @@ -1196,6 +1182,9 @@ importers: '@next/font': specifier: 13.1.6 version: 13.1.6 + '@sealos/shared': + specifier: workspace:^ + version: link:../../packages/shared '@sealos/ui': specifier: workspace:^ version: link:../../packages/ui @@ -1238,9 +1227,6 @@ importers: js-yaml: specifier: ^4.1.0 version: 4.1.0 - jsonwebtoken: - specifier: ^9.0.2 - version: 9.0.2 jszip: specifier: ^3.10.1 version: 3.10.1 @@ -1314,15 +1300,12 @@ importers: '@types/js-yaml': specifier: ^4.0.6 version: 4.0.9 - '@types/jsonwebtoken': - specifier: ^9.0.3 - version: 9.0.5 '@types/lodash': specifier: ^4.14.199 version: 4.14.202 '@types/node': - specifier: 18.13.0 - version: 18.13.0 + specifier: ^25.9.3 + version: 25.9.3 '@types/nprogress': specifier: ^0.2.1 version: 0.2.3 @@ -1443,9 +1426,6 @@ importers: js-yaml: specifier: ^4.1.0 version: 4.1.0 - jsonwebtoken: - specifier: ^9.0.2 - version: 9.0.2 lodash: specifier: ^4.17.21 version: 4.17.21 @@ -1516,15 +1496,12 @@ importers: '@types/js-yaml': specifier: ^4.0.6 version: 4.0.9 - '@types/jsonwebtoken': - specifier: ^9.0.3 - version: 9.0.5 '@types/lodash': specifier: ^4.14.199 version: 4.14.202 '@types/node': - specifier: 18.15.5 - version: 18.15.5 + specifier: ^25.9.3 + version: 25.9.3 '@types/nprogress': specifier: ^0.2.1 version: 0.2.3 @@ -1560,7 +1537,7 @@ importers: version: 5.1.4(typescript@5.2.2)(vite@7.2.6) vitest: specifier: ^4.0.12 - version: 4.0.15(@types/node@18.15.5)(@vitest/browser-playwright@4.0.15)(sass@1.69.5) + version: 4.0.15(@types/node@25.9.3)(@vitest/browser-playwright@4.0.15)(@vitest/ui@4.0.12)(jsdom@27.2.0)(sass@1.69.5) providers/cronjob: dependencies: @@ -1710,8 +1687,8 @@ importers: specifier: ^4.14.199 version: 4.14.202 '@types/node': - specifier: 18.13.0 - version: 18.13.0 + specifier: ^25.9.3 + version: 25.9.3 '@types/nprogress': specifier: ^0.2.1 version: 0.2.3 @@ -1747,7 +1724,7 @@ importers: version: 5.1.4(typescript@5.2.2)(vite@7.2.6) vitest: specifier: ^4.0.12 - version: 4.0.15(@types/node@18.13.0)(@vitest/browser-playwright@4.0.15)(@vitest/ui@4.0.12)(jsdom@27.2.0)(sass@1.69.5) + version: 4.0.15(@types/node@25.9.3)(@vitest/browser-playwright@4.0.15)(@vitest/ui@4.0.12)(jsdom@27.2.0)(sass@1.69.5) webpack-bundle-analyzer: specifier: ^4.9.1 version: 4.10.1 @@ -1805,9 +1782,6 @@ importers: '@tanstack/react-virtual': specifier: ^3.10.8 version: 3.10.8(react-dom@18.3.1)(react@18.3.1) - '@types/jsonwebtoken': - specifier: ^9.0.3 - version: 9.0.5 ansi_up: specifier: ^5.2.1 version: 5.2.1 @@ -1844,9 +1818,6 @@ importers: js-yaml: specifier: ^4.1.0 version: 4.1.0 - jsonwebtoken: - specifier: ^9.0.2 - version: 9.0.2 jszip: specifier: ^3.10.1 version: 3.10.1 @@ -1945,8 +1916,8 @@ importers: specifier: ^1.4.10 version: 1.4.11 '@types/node': - specifier: 18.13.0 - version: 18.13.0 + specifier: ^25.9.3 + version: 25.9.3 '@types/nprogress': specifier: ^0.2.1 version: 0.2.3 @@ -1985,7 +1956,7 @@ importers: version: 5.1.4(typescript@5.2.2)(vite@7.2.6) vitest: specifier: ^4.0.12 - version: 4.0.15(@types/node@18.13.0)(@vitest/browser-playwright@4.0.15)(@vitest/ui@4.0.12)(jsdom@27.2.0)(sass@1.69.5) + version: 4.0.15(@types/node@25.9.3)(@vitest/browser-playwright@4.0.15)(@vitest/ui@4.0.12)(jsdom@27.2.0)(sass@1.69.5) webpack-bundle-analyzer: specifier: ^4.9.1 version: 4.10.1 @@ -2129,8 +2100,8 @@ importers: specifier: ^4.14.199 version: 4.14.202 '@types/node': - specifier: 18.13.0 - version: 18.13.0 + specifier: ^25.9.3 + version: 25.9.3 '@types/nprogress': specifier: ^0.2.1 version: 0.2.3 @@ -2262,8 +2233,8 @@ importers: specifier: ^4.17.21 version: 4.17.21 '@types/node': - specifier: ^24.0.0 - version: 24.10.4 + specifier: ^25.9.3 + version: 25.9.3 '@types/nprogress': specifier: ^0.2.3 version: 0.2.3 @@ -2327,6 +2298,9 @@ importers: '@next/font': specifier: 13.1.6 version: 13.1.6 + '@sealos/shared': + specifier: workspace:^ + version: link:../../packages/shared '@sealos/ui': specifier: workspace:^ version: link:../../packages/ui @@ -2366,9 +2340,6 @@ importers: js-yaml: specifier: ^4.1.0 version: 4.1.0 - jsonwebtoken: - specifier: ^9.0.2 - version: 9.0.2 jszip: specifier: ^3.10.1 version: 3.10.1 @@ -2439,15 +2410,12 @@ importers: '@types/js-yaml': specifier: ^4.0.6 version: 4.0.9 - '@types/jsonwebtoken': - specifier: ^9.0.3 - version: 9.0.5 '@types/lodash': specifier: ^4.14.199 version: 4.14.202 '@types/node': - specifier: 18.13.0 - version: 18.13.0 + specifier: ^25.9.3 + version: 25.9.3 '@types/nprogress': specifier: ^0.2.1 version: 0.2.3 @@ -2547,9 +2515,6 @@ importers: js-yaml: specifier: ^4.1.0 version: 4.1.0 - jsonwebtoken: - specifier: ^9.0.2 - version: 9.0.2 jszip: specifier: ^3.10.1 version: 3.10.1 @@ -2605,15 +2570,12 @@ importers: '@types/js-yaml': specifier: ^4.0.6 version: 4.0.9 - '@types/jsonwebtoken': - specifier: ^9.0.3 - version: 9.0.5 '@types/lodash': specifier: ^4.14.199 version: 4.14.202 '@types/node': - specifier: ^20 - version: 20.10.0 + specifier: ^25.9.3 + version: 25.9.3 '@types/react': specifier: 18.3.27 version: 18.3.27 @@ -2640,7 +2602,7 @@ importers: version: 5.1.4(typescript@5.2.2)(vite@7.2.6) vitest: specifier: ^4.0.12 - version: 4.0.15(@types/node@20.10.0)(@vitest/browser-playwright@4.0.15) + version: 4.0.15(@types/node@25.9.3)(@vitest/browser-playwright@4.0.15) providers/template: dependencies: @@ -2853,8 +2815,8 @@ importers: specifier: ^4.14.199 version: 4.14.202 '@types/node': - specifier: 18.13.0 - version: 18.13.0 + specifier: ^25.9.3 + version: 25.9.3 '@types/nprogress': specifier: ^0.2.1 version: 0.2.3 @@ -2899,7 +2861,7 @@ importers: version: 5.1.4(typescript@5.2.2)(vite@7.2.6) vitest: specifier: ^4.0.12 - version: 4.0.15(@types/node@18.13.0)(@vitest/browser-playwright@4.0.15)(@vitest/ui@4.0.12)(jsdom@27.2.0)(sass@1.69.5) + version: 4.0.15(@types/node@25.9.3)(@vitest/browser-playwright@4.0.15)(@vitest/ui@4.0.12)(jsdom@27.2.0)(sass@1.69.5) providers/terminal: dependencies: @@ -3010,8 +2972,8 @@ importers: specifier: ^4.14.199 version: 4.14.202 '@types/node': - specifier: 18.15.5 - version: 18.15.5 + specifier: ^25.9.3 + version: 25.9.3 '@types/react': specifier: 18.3.27 version: 18.3.27 @@ -3035,7 +2997,7 @@ importers: version: 5.1.4(typescript@5.2.2)(vite@7.2.6) vitest: specifier: ^4.0.12 - version: 4.0.15(@types/node@18.15.5)(@vitest/browser-playwright@4.0.15)(sass@1.69.5) + version: 4.0.15(@types/node@25.9.3)(@vitest/browser-playwright@4.0.15)(@vitest/ui@4.0.12)(jsdom@27.2.0)(sass@1.69.5) packages: @@ -13217,14 +13179,14 @@ packages: /@types/base64-stream@1.0.5: resolution: {integrity: sha512-gXuo/a7pQ1EXlR5ksM2MccBLl6UUgAgnzR01r/QoHnkaSNinmzSdaGcCq5NAxn72dZ5A1zNYQIl+J9hPsBgBrA==} dependencies: - '@types/node': 20.17.32 + '@types/node': 25.9.3 dev: true /@types/body-parser@1.19.5: resolution: {integrity: sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==} dependencies: '@types/connect': 3.4.38 - '@types/node': 20.17.32 + '@types/node': 25.9.3 /@types/btoa-lite@1.0.2: resolution: {integrity: sha512-ZYbcE2x7yrvNFJiU7xJGrpF/ihpkM7zKgw8bha3LNJSesvTtUNxbpzaT7WXBIryf6jovisrxTBvymxMeLLj1Mg==} @@ -13243,7 +13205,7 @@ packages: /@types/connect@3.4.38: resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} dependencies: - '@types/node': 20.17.32 + '@types/node': 25.9.3 /@types/d3-array@3.2.2: resolution: {integrity: sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==} @@ -13344,7 +13306,7 @@ packages: /@types/dns-packet@5.6.5: resolution: {integrity: sha512-qXOC7XLOEe43ehtWJCMnQXvgcIpv6rPmQ1jXT98Ad8A3TB1Ue50jsCbSSSyuazScEuZ/Q026vHbrOTVkmwA+7Q==} dependencies: - '@types/node': 20.17.32 + '@types/node': 25.9.3 dev: true /@types/eslint-scope@3.7.7: @@ -13367,7 +13329,7 @@ packages: /@types/express-serve-static-core@4.17.41: resolution: {integrity: sha512-OaJ7XLaelTgrvlZD8/aa0vvvxZdUmlCn6MtWeB7TkiKW70BQLc9XEPpDLPdbo52ZhXUCrznlWdCHWxJWtdyajA==} dependencies: - '@types/node': 20.17.32 + '@types/node': 25.9.3 '@types/qs': 6.9.10 '@types/range-parser': 1.2.7 '@types/send': 0.17.4 @@ -13383,13 +13345,13 @@ packages: /@types/formidable@3.4.5: resolution: {integrity: sha512-s7YPsNVfnsng5L8sKnG/Gbb2tiwwJTY1conOkJzTMRvJAlLFW1nEua+ADsJQu8N1c0oTHx9+d5nqg10WuT9gHQ==} dependencies: - '@types/node': 20.17.32 + '@types/node': 25.9.3 dev: true /@types/fs-extra@8.1.5: resolution: {integrity: sha512-0dzKcwO+S8s2kuF5Z9oUWatQJj5Uq/iqphEtE3GQJVRRYm/tD1LglU2UnXi2A8jLq5umkGouOXOR9y0n613ZwQ==} dependencies: - '@types/node': 20.17.32 + '@types/node': 25.9.3 dev: true /@types/geojson@7946.0.14: @@ -13400,7 +13362,7 @@ packages: resolution: {integrity: sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==} dependencies: '@types/minimatch': 5.1.2 - '@types/node': 20.17.32 + '@types/node': 25.9.3 dev: true /@types/har-format@1.2.16: @@ -13449,7 +13411,8 @@ packages: /@types/jsonwebtoken@9.0.5: resolution: {integrity: sha512-VRLSGzik+Unrup6BsouBeHsf4d1hOEgYWTm/7Nmw1sXoN1+tRly/Gy/po3yeahnP4jfnQWWAhQAqcNfH7ngOkA==} dependencies: - '@types/node': 20.17.32 + '@types/node': 25.9.3 + dev: false /@types/lodash.mergewith@4.6.7: resolution: {integrity: sha512-3m+lkO5CLRRYU0fhGRp7zbsGi6+BZj0uTVSwvcKU+nSlhjA9/QRNfuSGnD2mX6hQA7ZbmcCkzk5h4ZYGOtk14A==} @@ -13504,7 +13467,7 @@ packages: /@types/node-fetch@2.6.12: resolution: {integrity: sha512-8nneRWKCg3rMtF69nLQJnOYUcbafYeFSjqkw3jCRLsqkWFlHaoQrr5mXmofFGOx3DKn7UfmBMyov8ySvLRVldA==} dependencies: - '@types/node': 20.17.32 + '@types/node': 25.9.3 form-data: 4.0.4 dev: false @@ -13520,27 +13483,14 @@ packages: resolution: {integrity: sha512-fAtCfv4jJg+ExtXhvCkCqUKZ+4ok/JQk01qDKhL5BDDoS3AxKXhV5/MAVUZyQnSEd2GT92fkgZl0pz0Q0AzcIQ==} dev: false - /@types/node@18.13.0: - resolution: {integrity: sha512-gC3TazRzGoOnoKAhUx+Q0t8S9Tzs74z7m0ipwGpSqQrleP14hKxP4/JUeEQcD3W1/aIpnWl8pHowI7WokuZpXg==} - dev: true - /@types/node@18.15.11: resolution: {integrity: sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q==} - /@types/node@18.15.5: - resolution: {integrity: sha512-Ark2WDjjZO7GmvsyFFf81MXuGTA/d6oP38anyxWOL6EREyBKAxKoFHwBhaZxCfLRLpO8JgVXwqOwSwa7jRcjew==} - dev: true - - /@types/node@20.10.0: - resolution: {integrity: sha512-D0WfRmU9TQ8I9PFx9Yc+EBHw+vSpIub4IDvQivcp26PtPrdMGAq5SDcpXEo/epqa/DXotVpekHiLNTg3iaKXBQ==} - dependencies: - undici-types: 5.26.5 - dev: true - /@types/node@20.17.32: resolution: {integrity: sha512-zeMXFn8zQ+UkjK4ws0RiOC9EWByyW1CcVmLe+2rQocXRsGEDxUCwPEIVgpsGcLHS/P8JkT0oa3839BRABS0oPw==} dependencies: undici-types: 6.19.8 + dev: false /@types/node@22.19.11: resolution: {integrity: sha512-BH7YwL6rA93ReqeQS1c4bsPpcfOmJasG+Fkr6Y59q83f9M1WcBRHR2vM+P9eOisYRcN3ujQoiZY8uk5W+1WL8w==} @@ -13548,16 +13498,15 @@ packages: undici-types: 6.21.0 dev: false - /@types/node@24.10.4: - resolution: {integrity: sha512-vnDVpYPMzs4wunl27jHrfmwojOGKya0xyM3sH+UE5iv5uPS6vX7UIoh6m+vQc5LGBq52HBKPIn/zcSZVzeDEZg==} + /@types/node@25.9.3: + resolution: {integrity: sha512-603BddQMv3pUcr4U2dhujk83N2tTDVr/34wII2B6bJy6g+8WD6yUb11jszNs0gdi4PesVWl7ABt8nYMVpnLUcg==} dependencies: - undici-types: 7.16.0 - dev: true + undici-types: 7.24.6 /@types/nodemailer@6.4.15: resolution: {integrity: sha512-0EBJxawVNjPkng1zm2vopRctuWVCxk34JcIlRuXSf54habUWdz1FB7wHDqOqvDa8Mtpt0Q3LTXQkAs2LNyK5jQ==} dependencies: - '@types/node': 20.17.32 + '@types/node': 25.9.3 dev: true /@types/nprogress@0.2.3: @@ -13567,7 +13516,7 @@ packages: /@types/papaparse@5.3.15: resolution: {integrity: sha512-JHe6vF6x/8Z85nCX4yFdDslN11d+1pr12E526X8WAfhadOeaOTx5AuIkvDKIBopfvlzpzkdMx4YyvSKCM9oqtw==} dependencies: - '@types/node': 20.17.32 + '@types/node': 25.9.3 dev: true /@types/parse-json@4.0.2: @@ -13619,7 +13568,7 @@ packages: resolution: {integrity: sha512-G3sY+NpsA9jnwm0ixhAFQSJ3Q9JkpLZpJbI3GMv0mIAT0y3mRabYeINzal5WOChIiaTEGQYlHOKgkaM9EisWHw==} dependencies: '@types/caseless': 0.12.5 - '@types/node': 20.17.32 + '@types/node': 25.9.3 '@types/tough-cookie': 4.0.5 form-data: 2.5.1 @@ -13635,19 +13584,19 @@ packages: resolution: {integrity: sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==} dependencies: '@types/mime': 1.3.5 - '@types/node': 20.17.32 + '@types/node': 25.9.3 /@types/serve-static@1.15.5: resolution: {integrity: sha512-PDRk21MnK70hja/YF8AHfC7yIsiQHn1rcXx7ijCFBX/k+XQJhQT/gw3xekXKJvx+5SXaMMS8oqQy09Mzvz2TuQ==} dependencies: '@types/http-errors': 2.0.4 '@types/mime': 3.0.4 - '@types/node': 20.17.32 + '@types/node': 25.9.3 /@types/stream-buffers@3.0.7: resolution: {integrity: sha512-azOCy05sXVXrO+qklf0c/B07H/oHaIuDDAiHPVwlk3A9Ek+ksHyTeMajLZl3r76FxpPpxem//4Te61G1iW3Giw==} dependencies: - '@types/node': 20.17.32 + '@types/node': 25.9.3 dev: false /@types/stylis@4.2.5: @@ -13667,7 +13616,7 @@ packages: /@types/tar@6.1.13: resolution: {integrity: sha512-IznnlmU5f4WcGTh2ltRu/Ijpmk8wiWXfF0VA4s+HPjHZgvFggk1YaIkbo5krX/zUCzWF8N/l4+W/LNxnvAJ8nw==} dependencies: - '@types/node': 20.17.32 + '@types/node': 25.9.3 minipass: 4.2.8 dev: false @@ -13715,25 +13664,25 @@ packages: /@types/whatwg-url@8.2.2: resolution: {integrity: sha512-FtQu10RWgn3D9U4aazdwIE2yzphmTJREDqNdODHrbrZmmMqI0vMheC/6NE/J1Yveaj8H+ela+YwWTjq5PGmuhA==} dependencies: - '@types/node': 20.17.32 + '@types/node': 25.9.3 '@types/webidl-conversions': 7.0.3 dev: false /@types/ws@6.0.4: resolution: {integrity: sha512-PpPrX7SZW9re6+Ha8ojZG4Se8AZXgf0GK6zmfqEuCsY49LFDNXO3SByp44X3dFEqtB73lkCDAdUazhAjVPiNwg==} dependencies: - '@types/node': 20.17.32 + '@types/node': 25.9.3 dev: false /@types/ws@8.5.10: resolution: {integrity: sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==} dependencies: - '@types/node': 20.17.32 + '@types/node': 25.9.3 /@types/xml2js@0.4.14: resolution: {integrity: sha512-4YnrRemBShWRO2QjvUin8ESA41rH+9nQGLUGZV/1IDhi3SL9OhdpNC/MrulTWuptXKwhx/aDxE7toV0f/ypIXQ==} dependencies: - '@types/node': 20.17.32 + '@types/node': 25.9.3 dev: false /@typescript-eslint/eslint-plugin@6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.33.0)(typescript@5.2.2): @@ -14068,7 +14017,7 @@ packages: debug: 4.4.3 globby: 11.1.0 is-glob: 4.0.3 - semver: 7.6.3 + semver: 7.7.4 tsutils: 3.21.0(typescript@5.2.2) typescript: 5.2.2 transitivePeerDependencies: @@ -14089,7 +14038,7 @@ packages: debug: 4.4.3 globby: 11.1.0 is-glob: 4.0.3 - semver: 7.6.3 + semver: 7.7.4 tsutils: 3.21.0(typescript@5.9.3) typescript: 5.9.3 transitivePeerDependencies: @@ -14276,7 +14225,7 @@ packages: '@rolldown/pluginutils': 1.0.0-beta.47 '@types/babel__core': 7.20.5 react-refresh: 0.18.0 - vite: 7.2.6(@types/node@18.15.11)(sass@1.69.5) + vite: 7.2.6(@types/node@25.9.3)(sass@1.69.5) transitivePeerDependencies: - supports-color dev: true @@ -14291,7 +14240,7 @@ packages: '@vitest/mocker': 4.0.15(vite@7.2.6) playwright: 1.59.1 tinyrainbow: 3.0.3 - vitest: 4.0.15(@types/node@18.15.11)(@vitest/browser-playwright@4.0.15)(sass@1.69.5) + vitest: 4.0.15(@types/node@25.9.3)(@vitest/browser-playwright@4.0.15)(@vitest/ui@4.0.12)(jsdom@27.2.0)(sass@1.69.5) transitivePeerDependencies: - bufferutil - msw @@ -14311,7 +14260,7 @@ packages: pngjs: 7.0.0 sirv: 3.0.2 tinyrainbow: 3.0.3 - vitest: 4.0.15(@types/node@18.15.11)(@vitest/browser-playwright@4.0.15)(sass@1.69.5) + vitest: 4.0.15(@types/node@25.9.3)(@vitest/browser-playwright@4.0.15)(@vitest/ui@4.0.12)(jsdom@27.2.0)(sass@1.69.5) ws: 8.18.3 transitivePeerDependencies: - bufferutil @@ -14345,7 +14294,7 @@ packages: '@vitest/spy': 4.0.15 estree-walker: 3.0.3 magic-string: 0.30.21 - vite: 7.2.6(@types/node@18.15.11)(sass@1.69.5) + vite: 7.2.6(@types/node@25.9.3)(sass@1.69.5) dev: true /@vitest/pretty-format@4.0.12: @@ -14391,7 +14340,7 @@ packages: sirv: 3.0.2 tinyglobby: 0.2.15 tinyrainbow: 3.0.3 - vitest: 4.0.15(@types/node@18.13.0)(@vitest/browser-playwright@4.0.15)(@vitest/ui@4.0.12)(jsdom@27.2.0)(sass@1.69.5) + vitest: 4.0.15(@types/node@25.9.3)(@vitest/browser-playwright@4.0.15)(@vitest/ui@4.0.12)(jsdom@27.2.0)(sass@1.69.5) dev: true /@vitest/utils@4.0.12: @@ -17190,6 +17139,13 @@ packages: zrender: 5.4.4 dev: false + /echarts@5.6.0: + resolution: {integrity: sha512-oTbVTsXfKuEhxftHqL5xprgLoc0k7uScAwtryCgWF6hPYFLRwOUHiFmHGCBKP5NPFNkDVopOieyUqYGH8Fa3kA==} + dependencies: + tslib: 2.3.0 + zrender: 5.6.1 + dev: false + /ee-first@1.0.3: resolution: {integrity: sha512-1q/3kz+ZwmrrWpJcCCrBZ3JnBzB1BMA5EVW9nxnIP1LxDZ16Cqs9VdolqLWlExet1vU+bar3WSkAa4/YrA9bIw==} dev: false @@ -21263,7 +21219,7 @@ packages: resolution: {integrity: sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==} engines: {node: '>= 10.13.0'} dependencies: - '@types/node': 20.17.32 + '@types/node': 25.9.3 merge-stream: 2.0.0 supports-color: 7.2.0 dev: true @@ -21272,7 +21228,7 @@ packages: resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==} engines: {node: '>= 10.13.0'} dependencies: - '@types/node': 20.17.32 + '@types/node': 25.9.3 merge-stream: 2.0.0 supports-color: 8.1.1 dev: false @@ -21504,11 +21460,11 @@ packages: engines: {node: '>=0.10.0'} dev: false - /jsonwebtoken@9.0.2: - resolution: {integrity: sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==} + /jsonwebtoken@9.0.3: + resolution: {integrity: sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g==} engines: {node: '>=12', npm: '>=6'} dependencies: - jws: 3.2.2 + jws: 4.0.1 lodash.includes: 4.3.0 lodash.isboolean: 3.0.3 lodash.isinteger: 4.0.4 @@ -21517,7 +21473,7 @@ packages: lodash.isstring: 4.0.1 lodash.once: 4.1.1 ms: 2.1.3 - semver: 7.6.3 + semver: 7.7.4 dev: false /jsprim@1.4.2: @@ -21559,18 +21515,18 @@ packages: resolution: {integrity: sha512-1IynUYEc/HAwxhi3WDpIpxJbZpMCvvrrmZVqvj9EhpvbH8lls7HhdhiByjL7DkAaWlLIzpC0Xc/VPvy/UxLNjA==} dev: false - /jwa@1.4.1: - resolution: {integrity: sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==} + /jwa@2.0.1: + resolution: {integrity: sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==} dependencies: buffer-equal-constant-time: 1.0.1 ecdsa-sig-formatter: 1.0.11 safe-buffer: 5.2.1 dev: false - /jws@3.2.2: - resolution: {integrity: sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==} + /jws@4.0.1: + resolution: {integrity: sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==} dependencies: - jwa: 1.4.1 + jwa: 2.0.1 safe-buffer: 5.2.1 dev: false @@ -23676,7 +23632,7 @@ packages: resolution: {integrity: sha512-p7eGEiQil0YUV3ItH4/tBb781L5impVmmx2E9FRKF7d18XXzp4PGT2tdYMFY6wQqgxD0IwNZOiSJ0/K0fSi/OA==} engines: {node: '>=10'} dependencies: - semver: 7.6.3 + semver: 7.7.4 dev: false /node-abort-controller@3.1.1: @@ -25851,7 +25807,6 @@ packages: resolution: {integrity: sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==} engines: {node: '>=10'} hasBin: true - dev: true /send@0.6.0: resolution: {integrity: sha512-A3EwHmDwcPcmLxIRNjr2YbXiYWq6M9JyUq4303pLKVFs4m5oeME0a9Cpcu9N22fED5XVepldjPYGo9eJifb7Yg==} @@ -27597,20 +27552,16 @@ packages: /underscore@1.13.6: resolution: {integrity: sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==} - /undici-types@5.26.5: - resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} - dev: true - /undici-types@6.19.8: resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==} + dev: false /undici-types@6.21.0: resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} dev: false - /undici-types@7.16.0: - resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==} - dev: true + /undici-types@7.24.6: + resolution: {integrity: sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==} /unhead@1.11.20: resolution: {integrity: sha512-3AsNQC0pjwlLqEYHLjtichGWankK8yqmocReITecmpB1H0aOabeESueyy+8X1gyJx4ftZVwo9hqQ4O3fPWffCA==} @@ -27749,7 +27700,7 @@ packages: resolution: {integrity: sha512-G33RTLrIBMFmlDV4u4CBF7dh71eWwykck4XgaxaIVeZKOYZRAAxvcGMRFTUclVY6xoUPQvO4Ne5wKGxYm/Yy9w==} dependencies: '@types/jsonwebtoken': 9.0.5 - jsonwebtoken: 9.0.2 + jsonwebtoken: 9.0.3 dev: false /universal-user-agent@6.0.1: @@ -28030,7 +27981,7 @@ packages: debug: 4.4.3 globrex: 0.1.2 tsconfck: 3.1.6(typescript@5.2.2) - vite: 7.2.6(@types/node@20.10.0) + vite: 7.2.6(@types/node@25.9.3)(sass@1.69.5) transitivePeerDependencies: - supports-color - typescript @@ -28047,117 +27998,13 @@ packages: debug: 4.4.3 globrex: 0.1.2 tsconfck: 3.1.6(typescript@5.9.3) - vite: 7.2.6(@types/node@18.15.11)(sass@1.69.5) + vite: 7.2.6(@types/node@25.9.3)(sass@1.69.5) transitivePeerDependencies: - supports-color - typescript dev: true - /vite@7.2.6(@types/node@18.13.0)(sass@1.69.5): - resolution: {integrity: sha512-tI2l/nFHC5rLh7+5+o7QjKjSR04ivXDF4jcgV0f/bTQ+OJiITy5S6gaynVsEM+7RqzufMnVbIon6Sr5x1SDYaQ==} - engines: {node: ^20.19.0 || >=22.12.0} - hasBin: true - peerDependencies: - '@types/node': ^20.19.0 || >=22.12.0 - jiti: '>=1.21.0' - less: ^4.0.0 - lightningcss: ^1.21.0 - sass: ^1.70.0 - sass-embedded: ^1.70.0 - stylus: '>=0.54.8' - sugarss: ^5.0.0 - terser: ^5.16.0 - tsx: ^4.8.1 - yaml: ^2.4.2 - peerDependenciesMeta: - '@types/node': - optional: true - jiti: - optional: true - less: - optional: true - lightningcss: - optional: true - sass: - optional: true - sass-embedded: - optional: true - stylus: - optional: true - sugarss: - optional: true - terser: - optional: true - tsx: - optional: true - yaml: - optional: true - dependencies: - '@types/node': 18.13.0 - esbuild: 0.25.12 - fdir: 6.5.0(picomatch@4.0.3) - picomatch: 4.0.3 - postcss: 8.5.6 - rollup: 4.53.3 - sass: 1.69.5 - tinyglobby: 0.2.15 - optionalDependencies: - fsevents: 2.3.3 - dev: true - - /vite@7.2.6(@types/node@18.15.11)(sass@1.69.5): - resolution: {integrity: sha512-tI2l/nFHC5rLh7+5+o7QjKjSR04ivXDF4jcgV0f/bTQ+OJiITy5S6gaynVsEM+7RqzufMnVbIon6Sr5x1SDYaQ==} - engines: {node: ^20.19.0 || >=22.12.0} - hasBin: true - peerDependencies: - '@types/node': ^20.19.0 || >=22.12.0 - jiti: '>=1.21.0' - less: ^4.0.0 - lightningcss: ^1.21.0 - sass: ^1.70.0 - sass-embedded: ^1.70.0 - stylus: '>=0.54.8' - sugarss: ^5.0.0 - terser: ^5.16.0 - tsx: ^4.8.1 - yaml: ^2.4.2 - peerDependenciesMeta: - '@types/node': - optional: true - jiti: - optional: true - less: - optional: true - lightningcss: - optional: true - sass: - optional: true - sass-embedded: - optional: true - stylus: - optional: true - sugarss: - optional: true - terser: - optional: true - tsx: - optional: true - yaml: - optional: true - dependencies: - '@types/node': 18.15.11 - esbuild: 0.25.12 - fdir: 6.5.0(picomatch@4.0.3) - picomatch: 4.0.3 - postcss: 8.5.6 - rollup: 4.53.3 - sass: 1.69.5 - tinyglobby: 0.2.15 - optionalDependencies: - fsevents: 2.3.3 - dev: true - - /vite@7.2.6(@types/node@18.15.5)(sass@1.69.5): + /vite@7.2.6(@types/node@25.9.3)(sass@1.69.5): resolution: {integrity: sha512-tI2l/nFHC5rLh7+5+o7QjKjSR04ivXDF4jcgV0f/bTQ+OJiITy5S6gaynVsEM+7RqzufMnVbIon6Sr5x1SDYaQ==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true @@ -28197,7 +28044,7 @@ packages: yaml: optional: true dependencies: - '@types/node': 18.15.5 + '@types/node': 25.9.3 esbuild: 0.25.12 fdir: 6.5.0(picomatch@4.0.3) picomatch: 4.0.3 @@ -28209,321 +28056,7 @@ packages: fsevents: 2.3.3 dev: true - /vite@7.2.6(@types/node@20.10.0): - resolution: {integrity: sha512-tI2l/nFHC5rLh7+5+o7QjKjSR04ivXDF4jcgV0f/bTQ+OJiITy5S6gaynVsEM+7RqzufMnVbIon6Sr5x1SDYaQ==} - engines: {node: ^20.19.0 || >=22.12.0} - hasBin: true - peerDependencies: - '@types/node': ^20.19.0 || >=22.12.0 - jiti: '>=1.21.0' - less: ^4.0.0 - lightningcss: ^1.21.0 - sass: ^1.70.0 - sass-embedded: ^1.70.0 - stylus: '>=0.54.8' - sugarss: ^5.0.0 - terser: ^5.16.0 - tsx: ^4.8.1 - yaml: ^2.4.2 - peerDependenciesMeta: - '@types/node': - optional: true - jiti: - optional: true - less: - optional: true - lightningcss: - optional: true - sass: - optional: true - sass-embedded: - optional: true - stylus: - optional: true - sugarss: - optional: true - terser: - optional: true - tsx: - optional: true - yaml: - optional: true - dependencies: - '@types/node': 20.10.0 - esbuild: 0.25.12 - fdir: 6.5.0(picomatch@4.0.3) - picomatch: 4.0.3 - postcss: 8.5.6 - rollup: 4.53.3 - tinyglobby: 0.2.15 - optionalDependencies: - fsevents: 2.3.3 - dev: true - - /vite@7.2.6(@types/node@20.17.32): - resolution: {integrity: sha512-tI2l/nFHC5rLh7+5+o7QjKjSR04ivXDF4jcgV0f/bTQ+OJiITy5S6gaynVsEM+7RqzufMnVbIon6Sr5x1SDYaQ==} - engines: {node: ^20.19.0 || >=22.12.0} - hasBin: true - peerDependencies: - '@types/node': ^20.19.0 || >=22.12.0 - jiti: '>=1.21.0' - less: ^4.0.0 - lightningcss: ^1.21.0 - sass: ^1.70.0 - sass-embedded: ^1.70.0 - stylus: '>=0.54.8' - sugarss: ^5.0.0 - terser: ^5.16.0 - tsx: ^4.8.1 - yaml: ^2.4.2 - peerDependenciesMeta: - '@types/node': - optional: true - jiti: - optional: true - less: - optional: true - lightningcss: - optional: true - sass: - optional: true - sass-embedded: - optional: true - stylus: - optional: true - sugarss: - optional: true - terser: - optional: true - tsx: - optional: true - yaml: - optional: true - dependencies: - '@types/node': 20.17.32 - esbuild: 0.25.12 - fdir: 6.5.0(picomatch@4.0.3) - picomatch: 4.0.3 - postcss: 8.5.6 - rollup: 4.53.3 - tinyglobby: 0.2.15 - optionalDependencies: - fsevents: 2.3.3 - dev: true - - /vitest@4.0.15(@types/node@18.13.0)(@vitest/browser-playwright@4.0.15)(@vitest/ui@4.0.12)(jsdom@27.2.0)(sass@1.69.5): - resolution: {integrity: sha512-n1RxDp8UJm6N0IbJLQo+yzLZ2sQCDyl1o0LeugbPWf8+8Fttp29GghsQBjYJVmWq3gBFfe9Hs1spR44vovn2wA==} - engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} - hasBin: true - peerDependencies: - '@edge-runtime/vm': '*' - '@opentelemetry/api': ^1.9.0 - '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 - '@vitest/browser-playwright': 4.0.15 - '@vitest/browser-preview': 4.0.15 - '@vitest/browser-webdriverio': 4.0.15 - '@vitest/ui': 4.0.15 - happy-dom: '*' - jsdom: '*' - peerDependenciesMeta: - '@edge-runtime/vm': - optional: true - '@opentelemetry/api': - optional: true - '@types/node': - optional: true - '@vitest/browser-playwright': - optional: true - '@vitest/browser-preview': - optional: true - '@vitest/browser-webdriverio': - optional: true - '@vitest/ui': - optional: true - happy-dom: - optional: true - jsdom: - optional: true - dependencies: - '@types/node': 18.13.0 - '@vitest/browser-playwright': 4.0.15(playwright@1.59.1)(vite@7.2.6)(vitest@4.0.15) - '@vitest/expect': 4.0.15 - '@vitest/mocker': 4.0.15(vite@7.2.6) - '@vitest/pretty-format': 4.0.15 - '@vitest/runner': 4.0.15 - '@vitest/snapshot': 4.0.15 - '@vitest/spy': 4.0.15 - '@vitest/ui': 4.0.12(vitest@4.0.15) - '@vitest/utils': 4.0.15 - es-module-lexer: 1.7.0 - expect-type: 1.2.2 - jsdom: 27.2.0 - magic-string: 0.30.21 - obug: 2.1.1 - pathe: 2.0.3 - picomatch: 4.0.3 - std-env: 3.10.0 - tinybench: 2.9.0 - tinyexec: 1.0.2 - tinyglobby: 0.2.15 - tinyrainbow: 3.0.3 - vite: 7.2.6(@types/node@18.13.0)(sass@1.69.5) - why-is-node-running: 2.3.0 - transitivePeerDependencies: - - jiti - - less - - lightningcss - - msw - - sass - - sass-embedded - - stylus - - sugarss - - terser - - tsx - - yaml - dev: true - - /vitest@4.0.15(@types/node@18.15.11)(@vitest/browser-playwright@4.0.15)(sass@1.69.5): - resolution: {integrity: sha512-n1RxDp8UJm6N0IbJLQo+yzLZ2sQCDyl1o0LeugbPWf8+8Fttp29GghsQBjYJVmWq3gBFfe9Hs1spR44vovn2wA==} - engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} - hasBin: true - peerDependencies: - '@edge-runtime/vm': '*' - '@opentelemetry/api': ^1.9.0 - '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 - '@vitest/browser-playwright': 4.0.15 - '@vitest/browser-preview': 4.0.15 - '@vitest/browser-webdriverio': 4.0.15 - '@vitest/ui': 4.0.15 - happy-dom: '*' - jsdom: '*' - peerDependenciesMeta: - '@edge-runtime/vm': - optional: true - '@opentelemetry/api': - optional: true - '@types/node': - optional: true - '@vitest/browser-playwright': - optional: true - '@vitest/browser-preview': - optional: true - '@vitest/browser-webdriverio': - optional: true - '@vitest/ui': - optional: true - happy-dom: - optional: true - jsdom: - optional: true - dependencies: - '@types/node': 18.15.11 - '@vitest/browser-playwright': 4.0.15(playwright@1.59.1)(vite@7.2.6)(vitest@4.0.15) - '@vitest/expect': 4.0.15 - '@vitest/mocker': 4.0.15(vite@7.2.6) - '@vitest/pretty-format': 4.0.15 - '@vitest/runner': 4.0.15 - '@vitest/snapshot': 4.0.15 - '@vitest/spy': 4.0.15 - '@vitest/utils': 4.0.15 - es-module-lexer: 1.7.0 - expect-type: 1.2.2 - magic-string: 0.30.21 - obug: 2.1.1 - pathe: 2.0.3 - picomatch: 4.0.3 - std-env: 3.10.0 - tinybench: 2.9.0 - tinyexec: 1.0.2 - tinyglobby: 0.2.15 - tinyrainbow: 3.0.3 - vite: 7.2.6(@types/node@18.15.11)(sass@1.69.5) - why-is-node-running: 2.3.0 - transitivePeerDependencies: - - jiti - - less - - lightningcss - - msw - - sass - - sass-embedded - - stylus - - sugarss - - terser - - tsx - - yaml - dev: true - - /vitest@4.0.15(@types/node@18.15.5)(@vitest/browser-playwright@4.0.15)(sass@1.69.5): - resolution: {integrity: sha512-n1RxDp8UJm6N0IbJLQo+yzLZ2sQCDyl1o0LeugbPWf8+8Fttp29GghsQBjYJVmWq3gBFfe9Hs1spR44vovn2wA==} - engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} - hasBin: true - peerDependencies: - '@edge-runtime/vm': '*' - '@opentelemetry/api': ^1.9.0 - '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 - '@vitest/browser-playwright': 4.0.15 - '@vitest/browser-preview': 4.0.15 - '@vitest/browser-webdriverio': 4.0.15 - '@vitest/ui': 4.0.15 - happy-dom: '*' - jsdom: '*' - peerDependenciesMeta: - '@edge-runtime/vm': - optional: true - '@opentelemetry/api': - optional: true - '@types/node': - optional: true - '@vitest/browser-playwright': - optional: true - '@vitest/browser-preview': - optional: true - '@vitest/browser-webdriverio': - optional: true - '@vitest/ui': - optional: true - happy-dom: - optional: true - jsdom: - optional: true - dependencies: - '@types/node': 18.15.5 - '@vitest/browser-playwright': 4.0.15(playwright@1.59.1)(vite@7.2.6)(vitest@4.0.15) - '@vitest/expect': 4.0.15 - '@vitest/mocker': 4.0.15(vite@7.2.6) - '@vitest/pretty-format': 4.0.15 - '@vitest/runner': 4.0.15 - '@vitest/snapshot': 4.0.15 - '@vitest/spy': 4.0.15 - '@vitest/utils': 4.0.15 - es-module-lexer: 1.7.0 - expect-type: 1.2.2 - magic-string: 0.30.21 - obug: 2.1.1 - pathe: 2.0.3 - picomatch: 4.0.3 - std-env: 3.10.0 - tinybench: 2.9.0 - tinyexec: 1.0.2 - tinyglobby: 0.2.15 - tinyrainbow: 3.0.3 - vite: 7.2.6(@types/node@18.15.5)(sass@1.69.5) - why-is-node-running: 2.3.0 - transitivePeerDependencies: - - jiti - - less - - lightningcss - - msw - - sass - - sass-embedded - - stylus - - sugarss - - terser - - tsx - - yaml - dev: true - - /vitest@4.0.15(@types/node@20.10.0)(@vitest/browser-playwright@4.0.15): + /vitest@4.0.15(@types/node@25.9.3)(@vitest/browser-playwright@4.0.15): resolution: {integrity: sha512-n1RxDp8UJm6N0IbJLQo+yzLZ2sQCDyl1o0LeugbPWf8+8Fttp29GghsQBjYJVmWq3gBFfe9Hs1spR44vovn2wA==} engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} hasBin: true @@ -28557,7 +28090,7 @@ packages: jsdom: optional: true dependencies: - '@types/node': 20.10.0 + '@types/node': 25.9.3 '@vitest/browser-playwright': 4.0.15(playwright@1.59.1)(vite@7.2.6)(vitest@4.0.15) '@vitest/expect': 4.0.15 '@vitest/mocker': 4.0.15(vite@7.2.6) @@ -28577,7 +28110,7 @@ packages: tinyexec: 1.0.2 tinyglobby: 0.2.15 tinyrainbow: 3.0.3 - vite: 7.2.6(@types/node@20.10.0) + vite: 7.2.6(@types/node@25.9.3)(sass@1.69.5) why-is-node-running: 2.3.0 transitivePeerDependencies: - jiti @@ -28593,7 +28126,7 @@ packages: - yaml dev: true - /vitest@4.0.15(@types/node@20.17.32)(@vitest/browser-playwright@4.0.15): + /vitest@4.0.15(@types/node@25.9.3)(@vitest/browser-playwright@4.0.15)(@vitest/ui@4.0.12)(jsdom@27.2.0)(sass@1.69.5): resolution: {integrity: sha512-n1RxDp8UJm6N0IbJLQo+yzLZ2sQCDyl1o0LeugbPWf8+8Fttp29GghsQBjYJVmWq3gBFfe9Hs1spR44vovn2wA==} engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} hasBin: true @@ -28627,7 +28160,7 @@ packages: jsdom: optional: true dependencies: - '@types/node': 20.17.32 + '@types/node': 25.9.3 '@vitest/browser-playwright': 4.0.15(playwright@1.59.1)(vite@7.2.6)(vitest@4.0.15) '@vitest/expect': 4.0.15 '@vitest/mocker': 4.0.15(vite@7.2.6) @@ -28635,9 +28168,11 @@ packages: '@vitest/runner': 4.0.15 '@vitest/snapshot': 4.0.15 '@vitest/spy': 4.0.15 + '@vitest/ui': 4.0.12(vitest@4.0.15) '@vitest/utils': 4.0.15 es-module-lexer: 1.7.0 expect-type: 1.2.2 + jsdom: 27.2.0 magic-string: 0.30.21 obug: 2.1.1 pathe: 2.0.3 @@ -28647,7 +28182,7 @@ packages: tinyexec: 1.0.2 tinyglobby: 0.2.15 tinyrainbow: 3.0.3 - vite: 7.2.6(@types/node@20.17.32) + vite: 7.2.6(@types/node@25.9.3)(sass@1.69.5) why-is-node-running: 2.3.0 transitivePeerDependencies: - jiti @@ -29286,6 +28821,12 @@ packages: tslib: 2.3.0 dev: false + /zrender@5.6.1: + resolution: {integrity: sha512-OFXkDJKcrlx5su2XbzJvj/34Q3m6PvyCZkVPHGYpcCJ52ek4U/ymZyfuV1nKE23AyBJ51E/6Yr0mhZ7xGTO4ag==} + dependencies: + tslib: 2.3.0 + dev: false + /zustand@4.4.7(@types/react@18.3.27)(immer@10.0.3)(react@18.3.1): resolution: {integrity: sha512-QFJWJMdlETcI69paJwhSMJz7PPWjVP8Sjhclxmxmxv/RYI7ZOvR5BHX+ktH0we9gTWQMxcne8q1OY8xxz604gw==} engines: {node: '>=12.7.0'} diff --git a/frontend/providers/aiproxy/app/[lng]/(user)/home/page.tsx b/frontend/providers/aiproxy/app/[lng]/(user)/home/page.tsx index 3ef570431dc6..5334a581d264 100644 --- a/frontend/providers/aiproxy/app/[lng]/(user)/home/page.tsx +++ b/frontend/providers/aiproxy/app/[lng]/(user)/home/page.tsx @@ -4,6 +4,7 @@ import { useState } from 'react'; import { Box, Button, Center, Flex, Text } from '@chakra-ui/react'; import { CurrencySymbol, MySelect } from '@sealos/ui'; import { useQuery, UseQueryResult } from '@tanstack/react-query'; +import dynamic from 'next/dynamic'; import { getDashboardData } from '@/api/platform'; import { useTranslationClientSide } from '@/app/i18n/client'; @@ -12,7 +13,9 @@ import { useBackendStore } from '@/store/backend'; import { QueryKey } from '@/types/query-key'; import { DashboardResponse } from '@/types/user/dashboard'; -import RequestDataChart from './components/RequestDataChart'; +const RequestDataChart = dynamic(() => import('./components/RequestDataChart'), { + ssr: false, +}); export default function Home(): React.JSX.Element { const { lng } = useI18n(); diff --git a/frontend/providers/aiproxy/package.json b/frontend/providers/aiproxy/package.json index 28b56027de54..93963ecad379 100644 --- a/frontend/providers/aiproxy/package.json +++ b/frontend/providers/aiproxy/package.json @@ -35,14 +35,13 @@ "axios": "^1.7.7", "date-fns": "^2.30.0", "downshift": "^9.0.8", - "echarts": "^5.4.3", + "echarts": "^5.6.0", "framer-motion": "^10.16.4", "i18next": "^23.11.5", "i18next-browser-languagedetector": "^8.0.0", "i18next-http-backend": "^2.6.2", "i18next-resources-to-backend": "^1.2.1", "immer": "^10.1.1", - "jsonwebtoken": "^9.0.2", "lucide-react": "^0.555.0", "next": "14.2.35", "next-i18next": "^15.3.0", @@ -63,13 +62,15 @@ "zustand": "^4.5.4" }, "devDependencies": { - "@types/jsonwebtoken": "^9.0.3", - "@types/node": "^20", + "@playwright/test": "^1.58.1", + "@types/node": "^25.9.3", "@types/react": "18.3.27", "@types/react-dom": "18.3.7", "@types/react-syntax-highlighter": "^15.5.7", "@types/swagger-ui-react": "^5.18.0", "@vitejs/plugin-react": "^5.1.1", + "@vitest/browser": "4.0.15", + "@vitest/browser-playwright": "4.0.15", "eslint": "^8", "eslint-config-next": "15.0.1", "eslint-config-prettier": "^9.1.0", @@ -78,9 +79,6 @@ "tailwindcss": "^3.4.1", "typescript": "^5", "vite-tsconfig-paths": "^5.1.4", - "vitest": "^4.0.12", - "@playwright/test": "^1.58.1", - "@vitest/browser": "4.0.15", - "@vitest/browser-playwright": "4.0.15" + "vitest": "^4.0.12" } } diff --git a/frontend/providers/aiproxy/tsconfig.json b/frontend/providers/aiproxy/tsconfig.json index 6e3b26607db5..9510caa43d9a 100644 --- a/frontend/providers/aiproxy/tsconfig.json +++ b/frontend/providers/aiproxy/tsconfig.json @@ -2,7 +2,7 @@ "compilerOptions": { "baseUrl": ".", "lib": ["dom", "dom.iterable", "esnext"], - "target": "ES2020", + "target": "esnext", "forceConsistentCasingInFileNames": false, "paths": { "@/*": ["./*"] diff --git a/frontend/providers/aiproxy/utils/backend/auth.ts b/frontend/providers/aiproxy/utils/backend/auth.ts index 71683dce60e7..1aa3475c73bb 100644 --- a/frontend/providers/aiproxy/utils/backend/auth.ts +++ b/frontend/providers/aiproxy/utils/backend/auth.ts @@ -1,4 +1,4 @@ -import jwt from 'jsonwebtoken' +import { isJwtToken, signJwt, verifyJwt } from '@sealos/shared/server/jwt' import { getNamespaceFromKubeConfigString, verifyK8sConfigString } from './check-kc' @@ -37,10 +37,13 @@ export async function kcOrAppTokenAuth(headers: Headers): Promise { if (isJwtToken(token)) { // Handle JWT token validation try { - const decoded = jwt.verify( + const decoded = await verifyJwt( token, global.AppConfig?.auth.appTokenJwtKey || process.env.APP_TOKEN_JWT_KEY || '' - ) as AppTokenPayload + ) + if (!decoded) { + return Promise.reject('Auth: Invalid JWT token') + } const now = Math.floor(Date.now() / 1000) if (decoded.exp && decoded.exp < now) { return Promise.reject('Auth: Token expired') @@ -80,15 +83,17 @@ export async function kcOrAppTokenAuthDecoded(headers: Headers): Promise if (token.includes('%')) { try { token = decodeURIComponent(token) - } catch (error) { - } + } catch {} } if (isJwtToken(token)) { try { - const decoded = jwt.verify( + const decoded = await verifyJwt( token, global.AppConfig?.auth.appTokenJwtKey || process.env.APP_TOKEN_JWT_KEY || '' - ) as AppTokenPayload + ) + if (!decoded) { + return Promise.reject('Auth: Invalid JWT token') + } const now = Math.floor(Date.now() / 1000) if (decoded.exp && decoded.exp < now) { return Promise.reject('Auth: Token expired') @@ -116,13 +121,6 @@ export async function kcOrAppTokenAuthDecoded(headers: Headers): Promise } } } -/** - * Check if the token is a JWT token (contains dots separating header, payload, signature) - */ -function isJwtToken(token: string): boolean { - return token.includes('.') && token.split('.').length === 3 -} - export async function parseJwtToken(headers: Headers): Promise { try { const token = headers.get('authorization') @@ -130,10 +128,13 @@ export async function parseJwtToken(headers: Headers): Promise { return Promise.reject('Auth: Token is missing') } - const decoded = jwt.verify( + const decoded = await verifyJwt( token, global.AppConfig?.auth.appTokenJwtKey || process.env.APP_TOKEN_JWT_KEY || '' - ) as AppTokenPayload + ) + if (!decoded) { + return Promise.reject('Auth: Invalid token') + } const now = Math.floor(Date.now() / 1000) if (decoded.exp && decoded.exp < now) { return Promise.reject('Auth: Token expired') @@ -166,10 +167,14 @@ export async function checkSealosUserIsRealName(headers: Headers): Promise( token, global.AppConfig?.auth.appTokenJwtKey || '' - ) as AppTokenPayload + ) + if (!decoded) { + console.error('CheckSealosUserIsRealName: Token is invalid') + return false + } const now = Math.floor(Date.now() / 1000) if (decoded.exp && decoded.exp < now) { console.error('CheckSealosUserIsRealName: Token expired') @@ -181,7 +186,7 @@ export async function checkSealosUserIsRealName(headers: Headers): Promise import('@/components/LogBarChart'), { + ssr: false +}); + export const LogCounts = ({ logCountsData, isLogCountsLoading diff --git a/frontend/providers/applaunchpad/src/components/apps/list/columns/CPU.tsx b/frontend/providers/applaunchpad/src/components/apps/list/columns/CPU.tsx index 9994dc8bc336..fd3e4872919e 100644 --- a/frontend/providers/applaunchpad/src/components/apps/list/columns/CPU.tsx +++ b/frontend/providers/applaunchpad/src/components/apps/list/columns/CPU.tsx @@ -1,10 +1,14 @@ import { memo } from 'react'; import { type CellContext } from '@tanstack/react-table'; +import dynamic from 'next/dynamic'; import { AppListItemType } from '@/types/app'; -import PodLineChart from '@/components/PodLineChart'; import { EMPTY_MONITOR_DATA } from '@/constants/monitor'; +const PodLineChart = dynamic(() => import('@/components/PodLineChart'), { + ssr: false +}); + export const CPU = memo>( ({ row }) => { const item = row.original; diff --git a/frontend/providers/applaunchpad/src/components/apps/list/columns/Memory.tsx b/frontend/providers/applaunchpad/src/components/apps/list/columns/Memory.tsx index 1f82d8a4f698..004e3acbc0ef 100644 --- a/frontend/providers/applaunchpad/src/components/apps/list/columns/Memory.tsx +++ b/frontend/providers/applaunchpad/src/components/apps/list/columns/Memory.tsx @@ -1,10 +1,14 @@ import { memo } from 'react'; import { type CellContext } from '@tanstack/react-table'; +import dynamic from 'next/dynamic'; import { AppListItemType } from '@/types/app'; -import PodLineChart from '@/components/PodLineChart'; import { EMPTY_MONITOR_DATA } from '@/constants/monitor'; +const PodLineChart = dynamic(() => import('@/components/PodLineChart'), { + ssr: false +}); + export const Memory = memo>( ({ row }) => { const item = row.original; diff --git a/frontend/providers/applaunchpad/src/pages/app/detail/monitor.tsx b/frontend/providers/applaunchpad/src/pages/app/detail/monitor.tsx index 3a61c6ddd42a..82cce5d1756f 100644 --- a/frontend/providers/applaunchpad/src/pages/app/detail/monitor.tsx +++ b/frontend/providers/applaunchpad/src/pages/app/detail/monitor.tsx @@ -4,7 +4,6 @@ import { serviceSideProps } from '@/utils/i18n'; import { useQuery } from '@tanstack/react-query'; import { useTranslation } from 'react-i18next'; import Header, { MonitorTab } from '@/components/Monitor/Header'; -import MonitorChart from '@/components/MonitorChart'; import { useCallback, useEffect, useMemo, useState } from 'react'; import { ListItem } from '@/components/AdvancedSelect'; import useDateTimeStore from '@/store/date'; @@ -15,8 +14,13 @@ import { generatePvcNameRegex } from '@/utils/tools'; import { parseTimeRange } from '@/utils/timeRange'; import { Skeleton } from '@sealos/shadcn-ui/skeleton'; import { Tabs, TabsContent, TabsList, TabsTrigger } from '@sealos/shadcn-ui/tabs'; +import dynamic from 'next/dynamic'; import { useRouter } from 'next/router'; +const MonitorChart = dynamic(() => import('@/components/MonitorChart'), { + ssr: false +}); + const getPaddedAxisMax = (value: { max: number }) => { const rawMax = value?.max ?? 0; if (rawMax <= 0) return 1; diff --git a/frontend/providers/applaunchpad/src/services/backend/auth.ts b/frontend/providers/applaunchpad/src/services/backend/auth.ts index 15358b12ed8c..d1aa5886bfc5 100644 --- a/frontend/providers/applaunchpad/src/services/backend/auth.ts +++ b/frontend/providers/applaunchpad/src/services/backend/auth.ts @@ -1,5 +1,5 @@ import { IncomingHttpHeaders } from 'http'; -import { verify } from 'jsonwebtoken'; +import { verifyJwt as verifySharedJwt } from '@sealos/shared/server/jwt'; export const authSession = async (header: IncomingHttpHeaders) => { if (!header) return Promise.reject('unAuthorization'); @@ -30,10 +30,5 @@ export const verifyJwt = async ( token: string, secret: string ): Promise => { - try { - const payload = verify(token, secret) as TPayload; - return payload; - } catch (err) { - return null; - } + return verifySharedJwt(token, secret) as Promise; }; diff --git a/frontend/providers/applaunchpad/src/types/request_schema.ts b/frontend/providers/applaunchpad/src/types/request_schema.ts index c3a8052ff541..6e6da9f91115 100644 --- a/frontend/providers/applaunchpad/src/types/request_schema.ts +++ b/frontend/providers/applaunchpad/src/types/request_schema.ts @@ -47,10 +47,7 @@ import { quantityToPublicCpuCores, quantityToPublicMemoryGi } from '@/utils/resourceQuantity'; -import { - APP_NAME_BASE_MAX_LENGTH, - APP_GENERATED_NAME_PATTERN -} from '@/utils/appNameValidation'; +import { APP_NAME_BASE_MAX_LENGTH, APP_GENERATED_NAME_PATTERN } from '@/utils/appNameValidation'; export const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz', 12); diff --git a/frontend/providers/applaunchpad/src/types/v2alpha/request_schema.ts b/frontend/providers/applaunchpad/src/types/v2alpha/request_schema.ts index e478bcce000f..0a727daa5009 100644 --- a/frontend/providers/applaunchpad/src/types/v2alpha/request_schema.ts +++ b/frontend/providers/applaunchpad/src/types/v2alpha/request_schema.ts @@ -25,10 +25,7 @@ import { quantityToPublicCpuCores, quantityToPublicMemoryGi } from '@/utils/resourceQuantity'; -import { - APP_NAME_BASE_MAX_LENGTH, - APP_GENERATED_NAME_PATTERN -} from '@/utils/appNameValidation'; +import { APP_NAME_BASE_MAX_LENGTH, APP_GENERATED_NAME_PATTERN } from '@/utils/appNameValidation'; export const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz', 12); diff --git a/frontend/providers/cloudserver/package.json b/frontend/providers/cloudserver/package.json index 6d57e86e5753..8c85eccad2e6 100644 --- a/frontend/providers/cloudserver/package.json +++ b/frontend/providers/cloudserver/package.json @@ -17,6 +17,7 @@ "@emotion/styled": "^11.11.0", "@kubernetes/client-node": "^0.18.1", "@next/font": "13.1.6", + "@sealos/shared": "workspace:^", "@sealos/ui": "workspace:^", "@tanstack/react-query": "^4.35.3", "@types/multer": "^1.4.10", @@ -31,7 +32,6 @@ "immer": "^9.0.21", "js-cookie": "^3.0.5", "js-yaml": "^4.1.0", - "jsonwebtoken": "^9.0.2", "jszip": "^3.10.1", "lodash": "^4.17.21", "minio": "^7.1.3", @@ -58,9 +58,8 @@ "@svgr/webpack": "^6.5.1", "@types/js-cookie": "^3.0.4", "@types/js-yaml": "^4.0.6", - "@types/jsonwebtoken": "^9.0.3", "@types/lodash": "^4.14.199", - "@types/node": "18.13.0", + "@types/node": "^25.9.3", "@types/nprogress": "^0.2.1", "@types/react": "18.3.27", "@types/react-dom": "18.3.7", diff --git a/frontend/providers/cloudserver/src/services/backend/auth.ts b/frontend/providers/cloudserver/src/services/backend/auth.ts index 4b99a0690a7e..74302c83c727 100644 --- a/frontend/providers/cloudserver/src/services/backend/auth.ts +++ b/frontend/providers/cloudserver/src/services/backend/auth.ts @@ -1,5 +1,5 @@ import { DesktopTokenPayload } from '@/types/user'; -import { verify } from 'jsonwebtoken'; +import { verifyJwtOrThrow } from '@sealos/shared/server/jwt'; import type { NextApiRequest } from 'next'; import { ERROR_ENUM } from '../error'; @@ -13,14 +13,4 @@ export const verifyAccessToken = async (req: NextApiRequest) => { }; export const verifyDesktopToken: (token: string) => Promise = (token) => - new Promise((resolve, reject) => { - verify(token, appJwtSecret, (err, payload) => { - if (err) { - reject(err); - } else if (!payload) { - reject('payload is null'); - } else { - resolve(payload as DesktopTokenPayload); - } - }); - }); + verifyJwtOrThrow(token, appJwtSecret); diff --git a/frontend/providers/costcenter/package.json b/frontend/providers/costcenter/package.json index 7b8908563adf..0efe8b1a3528 100644 --- a/frontend/providers/costcenter/package.json +++ b/frontend/providers/costcenter/package.json @@ -46,7 +46,6 @@ "immer": "^9.0.21", "js-cookie": "^3.0.5", "js-yaml": "^4.1.0", - "jsonwebtoken": "^9.0.2", "lodash": "^4.17.21", "lucide-react": "^0.540.0", "mongodb": "^5.9.0", @@ -71,9 +70,8 @@ "devDependencies": { "@types/js-cookie": "^3.0.4", "@types/js-yaml": "^4.0.6", - "@types/jsonwebtoken": "^9.0.3", "@types/lodash": "^4.14.199", - "@types/node": "18.15.5", + "@types/node": "^25.9.3", "@types/nprogress": "^0.2.1", "@types/react": "18.3.27", "@types/react-dom": "18.3.7", diff --git a/frontend/providers/costcenter/src/pages/api/plan/list.ts b/frontend/providers/costcenter/src/pages/api/plan/list.ts index ae054ed44316..68c2e2925013 100644 --- a/frontend/providers/costcenter/src/pages/api/plan/list.ts +++ b/frontend/providers/costcenter/src/pages/api/plan/list.ts @@ -5,7 +5,7 @@ import { PlanListResponse } from '@/types/plan'; export default async function handler(req: NextApiRequest, res: NextApiResponse) { try { - const client = makeAPIClient(null); + const client = await makeAPIClient(null); const response = await client.post( '/account/v1alpha1/workspace-subscription/plan-list' diff --git a/frontend/providers/costcenter/src/service/auth.ts b/frontend/providers/costcenter/src/service/auth.ts index ba06a022cedf..2c70c1f931f5 100644 --- a/frontend/providers/costcenter/src/service/auth.ts +++ b/frontend/providers/costcenter/src/service/auth.ts @@ -1,5 +1,5 @@ -import { sign, verify } from 'jsonwebtoken'; import { Config } from '@/config'; +import { signJwt, verifyJwt as verifySharedJwt } from '@sealos/shared/server/jwt'; const internalJwtSecret = () => Config().costCenter.auth.jwt.internal; @@ -17,22 +17,11 @@ export type AccessTokenPayload = { workspaceId: string; } & AuthenticationTokenPayload; -export const verifyJWT = (token: string, secret: string) => - new Promise((resolve) => { - if (!token) return resolve(null); - verify(token, secret, (err, payload) => { - if (err) { - resolve(null); - } else if (!payload) { - resolve(null); - } else { - resolve(payload as T); - } - }); - }); +export const verifyJWT = (token: string, secret: string) => + verifySharedJwt(token, secret); export const generateBillingToken = (props: AuthenticationTokenPayload) => - sign(props, billingJwtSecret(), { expiresIn: '5d' }); + signJwt(props, billingJwtSecret(), { expiresIn: '5d' }); export const verifyInternalToken = (token: string) => verifyJWT(token, internalJwtSecret()); diff --git a/frontend/providers/costcenter/src/service/backend/region.ts b/frontend/providers/costcenter/src/service/backend/region.ts index da2571989d19..8679a5aa0715 100644 --- a/frontend/providers/costcenter/src/service/backend/region.ts +++ b/frontend/providers/costcenter/src/service/backend/region.ts @@ -51,17 +51,16 @@ export async function makeRegionListAPIClientByHeader(req: NextApiRequest, res: return null; } const regionList = await getRegionList(); - const clientList = regionList?.map((region) => { - const client = makeAPIClient(region, payload); - return client; - }); + const clientList = regionList + ? await Promise.all(regionList.map((region) => makeAPIClient(region, payload))) + : undefined; return clientList; } -export function makeAPIClient( +export async function makeAPIClient( region: Region | undefined | null, payload?: AccessTokenPayload -): AxiosInstance { +): Promise { const baseURL = region?.accountSvc ? `http://${region?.accountSvc}` : Config().costCenter.components.billing.url; @@ -75,7 +74,7 @@ export function makeAPIClient( } }); } - const token = generateBillingToken({ + const token = await generateBillingToken({ userUid: payload.userUid, userId: payload.userId }); @@ -97,6 +96,6 @@ export async function makeAPIClientByHeader(req: NextApiRequest, res: NextApiRes return null; } const region = await getRegionByUid(regionUid); - const client = makeAPIClient(region, payload); + const client = await makeAPIClient(region, payload); return client; } diff --git a/frontend/providers/cronjob/package.json b/frontend/providers/cronjob/package.json index c5b7e8437c30..d025bfb254ca 100644 --- a/frontend/providers/cronjob/package.json +++ b/frontend/providers/cronjob/package.json @@ -66,7 +66,7 @@ "@types/js-cookie": "^3.0.4", "@types/js-yaml": "^4.0.6", "@types/lodash": "^4.14.199", - "@types/node": "18.13.0", + "@types/node": "^25.9.3", "@types/nprogress": "^0.2.1", "@types/react": "18.3.27", "@types/react-dom": "18.3.7", diff --git a/frontend/providers/dbprovider/package.json b/frontend/providers/dbprovider/package.json index 46e0bbbe8afe..57f6b7988022 100644 --- a/frontend/providers/dbprovider/package.json +++ b/frontend/providers/dbprovider/package.json @@ -35,7 +35,6 @@ "@tanstack/react-query": "^4.35.3", "@tanstack/react-table": "^8.10.7", "@tanstack/react-virtual": "^3.10.8", - "@types/jsonwebtoken": "^9.0.3", "ansi_up": "^5.2.1", "axios": "^1.5.1", "date-fns": "^3.0.0", @@ -48,7 +47,6 @@ "ini": "^4.1.3", "js-cookie": "^3.0.5", "js-yaml": "^4.1.0", - "jsonwebtoken": "^9.0.2", "jszip": "^3.10.1", "lodash": "^4.17.21", "lucide-react": "^0.511.0", @@ -83,7 +81,7 @@ "@types/js-yaml": "^4.0.6", "@types/lodash": "^4.14.199", "@types/multer": "^1.4.10", - "@types/node": "18.13.0", + "@types/node": "^25.9.3", "@types/nprogress": "^0.2.1", "@types/papaparse": "^5.3.15", "@types/react": "18.3.27", diff --git a/frontend/providers/dbprovider/src/services/backend/auth.ts b/frontend/providers/dbprovider/src/services/backend/auth.ts index 7e5d0731db50..ac269efac50c 100644 --- a/frontend/providers/dbprovider/src/services/backend/auth.ts +++ b/frontend/providers/dbprovider/src/services/backend/auth.ts @@ -1,7 +1,7 @@ import type { NextApiRequest } from 'next'; import { IncomingHttpHeaders } from 'http'; import { ResponseCode } from '@/types/response'; -import { verify } from 'jsonwebtoken'; +import { verifyJwt as verifySharedJwt } from '@sealos/shared/server/jwt'; export const authSession = async (req: NextApiRequest) => { if (!req.headers) return Promise.reject(ResponseCode.UNAUTHORIZED); @@ -32,10 +32,5 @@ export const verifyJwt = async ( token: string, secret: string ): Promise => { - try { - const payload = verify(token, secret) as TPayload; - return payload; - } catch (err) { - return null; - } + return verifySharedJwt(token, secret) as Promise; }; diff --git a/frontend/providers/invite/package.json b/frontend/providers/invite/package.json index 4650ee7fb510..0cf162217f9c 100644 --- a/frontend/providers/invite/package.json +++ b/frontend/providers/invite/package.json @@ -56,7 +56,7 @@ "@types/js-cookie": "^3.0.4", "@types/js-yaml": "^4.0.6", "@types/lodash": "^4.14.199", - "@types/node": "18.13.0", + "@types/node": "^25.9.3", "@types/nprogress": "^0.2.1", "@types/react": "18.3.27", "@types/react-dom": "18.3.7", diff --git a/frontend/providers/kubepanel/package.json b/frontend/providers/kubepanel/package.json index b64e5772a694..2b338bbb7565 100644 --- a/frontend/providers/kubepanel/package.json +++ b/frontend/providers/kubepanel/package.json @@ -45,7 +45,7 @@ "@tailwindcss/postcss": "^4.1.17", "@types/js-yaml": "^4.0.9", "@types/lodash": "^4.17.21", - "@types/node": "^24.0.0", + "@types/node": "^25.9.3", "@types/nprogress": "^0.2.3", "@types/react": "18.3.27", "autoprefixer": "^10.4.22", diff --git a/frontend/providers/kubepanel/tsconfig.json b/frontend/providers/kubepanel/tsconfig.json index 2ec2ce764ec5..ed828a5acd31 100644 --- a/frontend/providers/kubepanel/tsconfig.json +++ b/frontend/providers/kubepanel/tsconfig.json @@ -3,12 +3,8 @@ "noUncheckedIndexedAccess": true, "experimentalDecorators": true, "useDefineForClassFields": true, - "target": "ESNext", - "lib": [ - "dom", - "dom.iterable", - "esnext" - ], + "target": "esnext", + "lib": ["dom", "dom.iterable", "esnext"], "allowJs": true, "skipLibCheck": true, "strict": true, @@ -21,18 +17,10 @@ "jsx": "preserve", "incremental": true, "paths": { - "@/*": [ - "./src/*" - ] + "@/*": ["./src/*"] }, "forceConsistentCasingInFileNames": true }, - "include": [ - "next-env.d.ts", - "**/*.ts", - "**/*.tsx" - ], - "exclude": [ - "node_modules" - ] -} \ No newline at end of file + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"], + "exclude": ["node_modules"] +} diff --git a/frontend/providers/license/package.json b/frontend/providers/license/package.json index 26e83778286e..6d29664bb392 100644 --- a/frontend/providers/license/package.json +++ b/frontend/providers/license/package.json @@ -20,6 +20,7 @@ "@emotion/styled": "^11.11.0", "@kubernetes/client-node": "^0.18.1", "@next/font": "13.1.6", + "@sealos/shared": "workspace:^", "@sealos/ui": "workspace:^", "@tanstack/react-query": "^4.35.3", "ansi_up": "^5.2.1", @@ -33,7 +34,6 @@ "immer": "^9.0.21", "js-cookie": "^3.0.5", "js-yaml": "^4.1.0", - "jsonwebtoken": "^9.0.2", "jszip": "^3.10.1", "lodash": "^4.17.21", "mongodb": "^5.9.0", @@ -59,9 +59,8 @@ "@svgr/webpack": "^6.5.1", "@types/js-cookie": "^3.0.4", "@types/js-yaml": "^4.0.6", - "@types/jsonwebtoken": "^9.0.3", "@types/lodash": "^4.14.199", - "@types/node": "18.13.0", + "@types/node": "^25.9.3", "@types/nprogress": "^0.2.1", "@types/react": "18.3.27", "@types/react-dom": "18.3.7", diff --git a/frontend/providers/license/src/utils/crypto.ts b/frontend/providers/license/src/utils/crypto.ts index 78cf9b874833..f65b78ef2c66 100644 --- a/frontend/providers/license/src/utils/crypto.ts +++ b/frontend/providers/license/src/utils/crypto.ts @@ -1,6 +1,6 @@ import { LicenseToken } from '@/types'; +import { decodeJwt as decodeSharedJwt } from '@sealos/shared/server/jwt'; import * as crypto from 'crypto'; -import * as jwt from 'jsonwebtoken'; export const hashCrypto = (text: string): string => { const hash = crypto.createHash('sha256'); @@ -9,11 +9,5 @@ export const hashCrypto = (text: string): string => { }; export const decodeJWT = (token: string): T | null => { - try { - const decoded = jwt.decode(token); - return decoded as T; - } catch (error) { - console.error('Invalid token:', error); - return null; - } + return decodeSharedJwt(token) as T | null; }; diff --git a/frontend/providers/objectstorage/package.json b/frontend/providers/objectstorage/package.json index 87a778e67b24..23c3087b187b 100644 --- a/frontend/providers/objectstorage/package.json +++ b/frontend/providers/objectstorage/package.json @@ -39,7 +39,6 @@ "i18next": "^23.11.5", "immer": "^10.0.3", "js-yaml": "^4.1.0", - "jsonwebtoken": "^9.0.2", "jszip": "^3.10.1", "lodash": "^4.17.21", "lucide-react": "^0.544.0", @@ -60,9 +59,8 @@ "@playwright/test": "^1.58.1", "@types/js-cookie": "^3.0.4", "@types/js-yaml": "^4.0.6", - "@types/jsonwebtoken": "^9.0.3", "@types/lodash": "^4.14.199", - "@types/node": "^20", + "@types/node": "^25.9.3", "@types/react": "18.3.27", "@types/react-dom": "18.3.7", "@vitejs/plugin-react": "^5.1.1", diff --git a/frontend/providers/objectstorage/src/utils/isRealName.ts b/frontend/providers/objectstorage/src/utils/isRealName.ts index ca0f7ad67861..baea65942f5d 100644 --- a/frontend/providers/objectstorage/src/utils/isRealName.ts +++ b/frontend/providers/objectstorage/src/utils/isRealName.ts @@ -1,5 +1,5 @@ -import jwt from 'jsonwebtoken'; import { Config } from '@/config'; +import { signJwt, verifyJwt } from '@sealos/shared/server/jwt'; interface AppTokenPayload { workspaceUid: string; @@ -49,7 +49,11 @@ export async function checkSealosUserIsRealName(token: string): Promise return false; } - const decoded = jwt.verify(token, sealosAppTokenJwtKey) as AppTokenPayload; + const decoded = await verifyJwt(token, sealosAppTokenJwtKey); + if (!decoded) { + console.error('CheckSealosUserIsRealName: Token is invalid'); + return false; + } const now = Math.floor(Date.now() / 1000); if (decoded.exp && decoded.exp < now) { console.error('CheckSealosUserIsRealName: Token expired'); @@ -61,7 +65,7 @@ export async function checkSealosUserIsRealName(token: string): Promise return false; } - const accountServerToken = jwt.sign( + const accountServerToken = await signJwt( { userUid: decoded.userUid, userId: decoded.userId diff --git a/frontend/providers/template/package.json b/frontend/providers/template/package.json index c5a43433d02c..ebf7610082a2 100644 --- a/frontend/providers/template/package.json +++ b/frontend/providers/template/package.json @@ -85,7 +85,7 @@ "@types/js-cookie": "^3.0.4", "@types/js-yaml": "^4.0.6", "@types/lodash": "^4.14.199", - "@types/node": "18.13.0", + "@types/node": "^25.9.3", "@types/nprogress": "^0.2.1", "@types/pluralize": "^0.0.33", "@types/react": "18.3.27", diff --git a/frontend/providers/terminal/package.json b/frontend/providers/terminal/package.json index 49139ddb92cf..e6e3529dca15 100644 --- a/frontend/providers/terminal/package.json +++ b/frontend/providers/terminal/package.json @@ -51,7 +51,7 @@ "@playwright/test": "^1.58.1", "@types/js-yaml": "^4.0.6", "@types/lodash": "^4.14.199", - "@types/node": "18.15.5", + "@types/node": "^25.9.3", "@types/react": "18.3.27", "@types/react-dom": "18.3.7", "@vitejs/plugin-react": "^5.1.1", diff --git a/frontend/tsconfig.base.json b/frontend/tsconfig.base.json index e7c73a3c31ba..18b7261a053a 100644 --- a/frontend/tsconfig.base.json +++ b/frontend/tsconfig.base.json @@ -1,6 +1,6 @@ { "compilerOptions": { - "target": "es2022", + "target": "esnext", "module": "esnext", "noEmit": true, "moduleResolution": "node",