diff --git a/package-lock.json b/package-lock.json index c72724e2..9a151c56 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3363,9 +3363,9 @@ "license": "MIT" }, "node_modules/@next/eslint-plugin-next": { - "version": "16.3.8", - "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-16.3.8.tgz", - "integrity": "sha512-eCR9RcLTZrVS+K3+VOhi0xMdUUGNuriCHHhU1ZoABC/QQ/Gp0OWbLKQxV2/9DkmihYdYRcQWFmZXW8M1AMUXYw==", + "version": "16.4.2", + "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-16.4.2.tgz", + "integrity": "sha512-gCTWlw0e+Y6kajpFyKKgA195jUJCRhlzJE0lV7ufe8a+ghIsANWZ0/FiJwg6DuOG1+OvRbcZDcFBoRh3mNJx0g==", "dev": true, "license": "MIT", "dependencies": { @@ -13818,21 +13818,21 @@ } }, "node_modules/eslint-config-next": { - "version": "16.3.8", - "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-16.3.8.tgz", - "integrity": "sha512-81vovwMe6NGnoFsl0KUJWzlS+y239i3fdsxs49gSEX3pDQW2cuKy+fxUPXNkQqqx6OCPheYPtBlSufc9aZ0k+w==", + "version": "16.4.2", + "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-16.4.2.tgz", + "integrity": "sha512-P33vjcETMXc+qBjpF77TeJHZLzk6cPcTy8KjpxLucYB3mCIFIORMcZ+cBXcAiIYjE5RM7jyknEEOwMNdnx/ADQ==", "dev": true, "license": "MIT", "dependencies": { - "@next/eslint-plugin-next": "16.3.8", + "@next/eslint-plugin-next": "16.4.2", "eslint-import-resolver-node": "^0.3.6", "eslint-import-resolver-typescript": "^3.5.2", "eslint-plugin-import": "^2.32.0", "eslint-plugin-jsx-a11y": "^6.10.0", "eslint-plugin-react": "^7.37.0", - "eslint-plugin-react-hooks": "^7.0.0", + "eslint-plugin-react-hooks": "^7.1.0", "globals": "16.4.0", - "typescript-eslint": "^8.46.0" + "typescript-eslint": "^8.56.0" }, "peerDependencies": { "eslint": ">=9.0.0", @@ -14086,9 +14086,9 @@ } }, "node_modules/eslint-plugin-react-hooks": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.0.1.tgz", - "integrity": "sha512-O0d0m04evaNzEPoSW+59Mezf8Qt0InfgGIBJnpC0h3NH/WjUAR7BIKUfysC6todmtiZ/A0oUVS8Gce0WhBrHsA==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.1.1.tgz", + "integrity": "sha512-f2I7Gw6JbvCexzIInuSbZpfdQ44D7iqdWX01FKLvrPgqxoE7oMj8clOfto8U6vYiz4yd5oKu39rRSVOe1zRu0g==", "dev": true, "license": "MIT", "dependencies": { @@ -14102,7 +14102,7 @@ "node": ">=18" }, "peerDependencies": { - "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 || ^10.0.0" } }, "node_modules/eslint-scope": { @@ -20785,9 +20785,9 @@ } }, "node_modules/prettier": { - "version": "3.9.9", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.9.9.tgz", - "integrity": "sha512-Z/CJHIkdujO/OtN7nXUii0Rf3VT5SRuhjBA82Xvu2XhBUgX3nhP67T0LHceBdQLex7OOFGTox+Q5Yg8Jk2Qivg==", + "version": "3.9.10", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.9.10.tgz", + "integrity": "sha512-skGHTH1Yb9QvaTC4hWnRTzEYtCpApF0UMBPz+t/0ZPzP8ihefo/btKd+QA7pUE0fEwA1SbS/NmhyEcstdXOIdA==", "dev": true, "license": "MIT", "bin": { @@ -25065,7 +25065,7 @@ "eslint": "^9.39.5", "jest": "^30.5.2", "pino-pretty": "^13.1.3", - "prettier": "^3.9.9", + "prettier": "^3.9.10", "prisma": "^7.10.1", "source-map-support": "^0.5.21", "ts-jest": "^29.4.15", @@ -25332,7 +25332,7 @@ "@types/react-dom": "^19.3.0", "autoprefixer": "^10.6.1", "eslint": "^9.39.5", - "eslint-config-next": "^16.3.8", + "eslint-config-next": "^16.4.0", "postcss": "^8.5.28", "tailwindcss": "^4.0.0", "typescript": "^5.9.3" diff --git a/packages/backend/package.json b/packages/backend/package.json index b4bae9f6..519df572 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -101,7 +101,7 @@ "eslint": "^9.39.5", "jest": "^30.5.2", "pino-pretty": "^13.1.3", - "prettier": "^3.9.9", + "prettier": "^3.9.10", "prisma": "^7.10.1", "source-map-support": "^0.5.21", "ts-jest": "^29.4.15", diff --git a/packages/frontend/package.json b/packages/frontend/package.json index 5612064d..9e1863c5 100644 --- a/packages/frontend/package.json +++ b/packages/frontend/package.json @@ -43,7 +43,7 @@ "@types/react-dom": "^19.3.0", "autoprefixer": "^10.6.1", "eslint": "^9.39.5", - "eslint-config-next": "^16.3.8", + "eslint-config-next": "^16.4.0", "postcss": "^8.5.28", "tailwindcss": "^4.0.0", "typescript": "^5.9.3" diff --git a/packages/frontend/src/app/connectors/[id]/page.tsx b/packages/frontend/src/app/connectors/[id]/page.tsx index 3241e118..28a0bb98 100644 --- a/packages/frontend/src/app/connectors/[id]/page.tsx +++ b/packages/frontend/src/app/connectors/[id]/page.tsx @@ -290,6 +290,13 @@ export default function ConnectorDetailPage() { >([]); const [savingEnvVars, setSavingEnvVars] = useState(false); + const resetOauth1Fields = () => { + setEditOauth1Key(''); + setEditOauth1Secret(''); + setEditOauth1Token(''); + setEditOauth1TokenSecret(''); + }; + const fetchConnector = async () => { if (!token) return; try { @@ -404,13 +411,6 @@ export default function ConnectorDetailPage() { fetchConnector(); }, [token, id]); - const resetOauth1Fields = () => { - setEditOauth1Key(''); - setEditOauth1Secret(''); - setEditOauth1Token(''); - setEditOauth1TokenSecret(''); - }; - const buildAuthConfig = () => { // Only send authConfig if the user filled in credential fields; // empty fields mean "keep existing credentials on the server". diff --git a/packages/frontend/src/app/settings/identity-providers/page.tsx b/packages/frontend/src/app/settings/identity-providers/page.tsx index a8ab6abc..1537e948 100644 --- a/packages/frontend/src/app/settings/identity-providers/page.tsx +++ b/packages/frontend/src/app/settings/identity-providers/page.tsx @@ -128,6 +128,13 @@ const emptyForm = (): IdentityProviderInput & { config: Record } roleSyncDefaultRoleIds: [], }); +// True when the client secret runs out within 30 days. +function expiringSoon(p: IdentityProvider): boolean { + if (!p.clientSecretExpiresAt) return false; + const days = (new Date(p.clientSecretExpiresAt).getTime() - Date.now()) / 86_400_000; + return days < 30; +} + export default function IdentityProvidersPage() { const { token, user: currentUser, deploymentMode, deploymentModeLoaded } = useAuth(); const toast = useToast(); @@ -370,13 +377,6 @@ export default function IdentityProvidersPage() { `${typeof window === 'undefined' ? '' : window.location.origin}/sso/${p.initiateId}`; const spec = PROVIDER_TYPES[form.type]; - const expiringSoon = (p: IdentityProvider) => { - if (!p.clientSecretExpiresAt) return false; - const days = - (new Date(p.clientSecretExpiresAt).getTime() - Date.now()) / 86_400_000; - return days < 30; - }; - return (
{locked && ( diff --git a/packages/frontend/src/app/settings/identity-providers/scim-panel.tsx b/packages/frontend/src/app/settings/identity-providers/scim-panel.tsx index 0142f367..99693079 100644 --- a/packages/frontend/src/app/settings/identity-providers/scim-panel.tsx +++ b/packages/frontend/src/app/settings/identity-providers/scim-panel.tsx @@ -6,6 +6,11 @@ import { Button } from '@/components/ui/button'; import { Badge } from '@/components/ui/badge'; import { useToast } from '@/components/toast'; +// True when the identity provider has not called in for over two hours. +function isStale(lastRequestAt: string): boolean { + return Date.now() - new Date(lastRequestAt).getTime() > 2 * 3600_000; +} + /** * Provisioning (SCIM) for one provider. * @@ -124,10 +129,7 @@ export function ScimPanel({ const labelClass = 'block text-[11.5px] font-medium text-[var(--text-3)] mb-1'; const codeClass = 'text-[11.5px] text-[var(--text-2)] bg-[var(--surface-2)] rounded px-1.5 py-0.5 break-all'; - const stale = - status?.enabled && status.lastRequestAt - ? Date.now() - new Date(status.lastRequestAt).getTime() > 2 * 3600_000 - : false; + const stale = status?.enabled && status.lastRequestAt ? isStale(status.lastRequestAt) : false; return (
diff --git a/packages/frontend/src/app/settings/page.tsx b/packages/frontend/src/app/settings/page.tsx index 56375203..db3bf322 100644 --- a/packages/frontend/src/app/settings/page.tsx +++ b/packages/frontend/src/app/settings/page.tsx @@ -147,7 +147,7 @@ export default function SettingsPage() { const { authorizationUrl } = await sso.startLink(p.id, '/settings', token); // Top-level navigation, not fetch: the provider needs to render its own // sign-in page to the user. - window.location.href = authorizationUrl; + window.location.assign(authorizationUrl); } catch (err: any) { toast.show({ tone: 'error', title: 'Could not connect', description: err.message }); setLinkBusy(null);