From 8a5a40676afce4eee381bc5ec697db8f2b359a72 Mon Sep 17 00:00:00 2001 From: saravanakumardb1 Date: Sat, 28 Feb 2026 11:28:14 -0800 Subject: [PATCH] refactor(web): wire @bytelyst/react-auth into auth-context, clean platform-sync auth plumbing --- web/package-lock.json | 172 ++++++------------------ web/package.json | 2 + web/src/lib/auth-context.tsx | 246 ++++++++--------------------------- web/src/lib/platform-sync.ts | 37 +----- 4 files changed, 99 insertions(+), 358 deletions(-) diff --git a/web/package-lock.json b/web/package-lock.json index d5c0cb4..606336b 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -8,7 +8,9 @@ "name": "web", "version": "0.1.0", "dependencies": { + "@bytelyst/api-client": "file:../../learning_ai_common_plat/packages/api-client", "@bytelyst/auth-client": "file:../../learning_ai_common_plat/packages/auth-client", + "@bytelyst/react-auth": "file:../../learning_ai_common_plat/packages/react-auth", "@bytelyst/telemetry-client": "file:../../learning_ai_common_plat/packages/telemetry-client", "@serwist/next": "^9.5.6", "date-fns": "^4.1.0", @@ -40,10 +42,32 @@ "vitest": "^4.0.18" } }, + "../../learning_ai_common_plat/packages/api-client": { + "name": "@bytelyst/api-client", + "version": "0.1.0" + }, "../../learning_ai_common_plat/packages/auth-client": { "name": "@bytelyst/auth-client", "version": "0.1.0" }, + "../../learning_ai_common_plat/packages/react-auth": { + "name": "@bytelyst/react-auth", + "version": "0.1.0", + "dependencies": { + "@bytelyst/api-client": "workspace:*" + }, + "devDependencies": { + "@testing-library/react": "^16.3.2", + "@types/react": "^19.2.14", + "@types/react-dom": "^19.2.3", + "happy-dom": "^18.0.1", + "react": "^19.2.4", + "react-dom": "^19.2.4" + }, + "peerDependencies": { + "react": ">=18.0.0" + } + }, "../../learning_ai_common_plat/packages/telemetry-client": { "name": "@bytelyst/telemetry-client", "version": "0.1.0" @@ -164,7 +188,6 @@ "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", @@ -397,10 +420,18 @@ "specificity": "bin/cli.js" } }, + "node_modules/@bytelyst/api-client": { + "resolved": "../../learning_ai_common_plat/packages/api-client", + "link": true + }, "node_modules/@bytelyst/auth-client": { "resolved": "../../learning_ai_common_plat/packages/auth-client", "link": true }, + "node_modules/@bytelyst/react-auth": { + "resolved": "../../learning_ai_common_plat/packages/react-auth", + "link": true + }, "node_modules/@bytelyst/telemetry-client": { "resolved": "../../learning_ai_common_plat/packages/telemetry-client", "link": true @@ -493,7 +524,6 @@ } ], "license": "MIT", - "peer": true, "engines": { "node": ">=20.19.0" }, @@ -534,7 +564,6 @@ } ], "license": "MIT", - "peer": true, "engines": { "node": ">=20.19.0" } @@ -1980,9 +2009,8 @@ "version": "1.58.2", "resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/@playwright/test/-/test-1.58.2.tgz", "integrity": "sha512-akea+6bHYBBfA9uQqSYmlJXn61cTa+jbO87xVLCWbTqbWadRVmhxlXATaOjOgcBaWU4ePo0wB41KMFv3o35IXA==", - "devOptional": true, + "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "playwright": "1.58.2" }, @@ -2809,36 +2837,6 @@ "tailwindcss": "4.2.1" } }, - "node_modules/@testing-library/dom": { - "version": "10.4.1", - "resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/@testing-library/dom/-/dom-10.4.1.tgz", - "integrity": "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.10.4", - "@babel/runtime": "^7.12.5", - "@types/aria-query": "^5.0.1", - "aria-query": "5.3.0", - "dom-accessibility-api": "^0.5.9", - "lz-string": "^1.5.0", - "picocolors": "1.1.1", - "pretty-format": "^27.0.2" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@testing-library/dom/node_modules/aria-query": { - "version": "5.3.0", - "resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/aria-query/-/aria-query-5.3.0.tgz", - "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "dequal": "^2.0.3" - } - }, "node_modules/@testing-library/jest-dom": { "version": "6.9.1", "resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/@testing-library/jest-dom/-/jest-dom-6.9.1.tgz", @@ -2905,13 +2903,6 @@ "tslib": "^2.4.0" } }, - "node_modules/@types/aria-query": { - "version": "5.0.4", - "resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/@types/aria-query/-/aria-query-5.0.4.tgz", - "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", - "dev": true, - "license": "MIT" - }, "node_modules/@types/chai": { "version": "5.2.3", "resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/@types/chai/-/chai-5.2.3.tgz", @@ -3020,7 +3011,6 @@ "integrity": "sha512-Uarfe6J91b9HAUXxjvSOdiO2UPOKLm07Q1oh0JHxoZ1y8HoqxDAu3gVrsrOHeiio0kSsoVBt4wFrKOm0dKxVPQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "undici-types": "~6.21.0" } @@ -3029,9 +3019,8 @@ "version": "19.2.14", "resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/@types/react/-/react-19.2.14.tgz", "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", - "devOptional": true, + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "csstype": "^3.2.2" } @@ -3042,7 +3031,6 @@ "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", "dev": true, "license": "MIT", - "peer": true, "peerDependencies": { "@types/react": "^19.2.0" } @@ -3111,7 +3099,6 @@ "integrity": "sha512-klQbnPAAiGYFyI02+znpBRLyjL4/BrBd0nyWkdC0s/6xFLkXYQ8OoRrSkqacS1ddVxf/LDyODIKbQ5TgKAf/Fg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.56.1", "@typescript-eslint/types": "8.56.1", @@ -3748,7 +3735,6 @@ "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", "dev": true, "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -4128,7 +4114,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -4371,7 +4356,7 @@ "version": "3.2.3", "resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/csstype/-/csstype-3.2.3.tgz", "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", - "devOptional": true, + "dev": true, "license": "MIT" }, "node_modules/d3-array": { @@ -4654,16 +4639,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/dequal": { - "version": "2.0.3", - "resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/dequal/-/dequal-2.0.3.tgz", - "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/detect-libc": { "version": "2.1.2", "resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/detect-libc/-/detect-libc-2.1.2.tgz", @@ -4687,13 +4662,6 @@ "node": ">=0.10.0" } }, - "node_modules/dom-accessibility-api": { - "version": "0.5.16", - "resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", - "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", - "dev": true, - "license": "MIT" - }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/dunder-proto/-/dunder-proto-1.0.1.tgz", @@ -5018,7 +4986,6 @@ "integrity": "sha512-VmQ+sifHUbI/IcSopBCF/HO3YiHQx/AVd3UVyYL6weuwW+HvON9VYn5l6Zl1WZzPWXPNZrSQpxwkkZ/VuvJZzg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -5204,7 +5171,6 @@ "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@rtsao/scc": "^1.1.0", "array-includes": "^3.1.9", @@ -6633,7 +6599,6 @@ "integrity": "sha512-0+MoQNYyr2rBHqO1xilltfDjV9G7ymYGlAUazgcDLQaUf8JDHbuGwsxN6U9qWaElZ4w1B2r7yEGIL3GdeW3Rug==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@acemir/cssom": "^0.9.31", "@asamuzakjp/dom-selector": "^6.8.1", @@ -7104,16 +7069,6 @@ "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, - "node_modules/lz-string": { - "version": "1.5.0", - "resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/lz-string/-/lz-string-1.5.0.tgz", - "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", - "dev": true, - "license": "MIT", - "bin": { - "lz-string": "bin/bin.js" - } - }, "node_modules/magic-string": { "version": "0.30.21", "resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/magic-string/-/magic-string-0.30.21.tgz", @@ -7260,7 +7215,6 @@ "resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/next/-/next-16.1.6.tgz", "integrity": "sha512-hkyRkcu5x/41KoqnROkfTm2pZVbKxvbZRuNvKXLRXxs3VfyO0WhY50TQS40EuKO9SW3rBj/sF3WbVwDACeMZyw==", "license": "MIT", - "peer": true, "dependencies": { "@next/env": "16.1.6", "@swc/helpers": "0.5.15", @@ -7674,7 +7628,7 @@ "version": "1.58.2", "resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/playwright/-/playwright-1.58.2.tgz", "integrity": "sha512-vA30H8Nvkq/cPBnNw4Q8TWz1EJyqgpuinBcHET0YVJVFldr8JDNiU9LaWAE1KqSkRYazuaBhTpB5ZzShOezQ6A==", - "devOptional": true, + "dev": true, "license": "Apache-2.0", "dependencies": { "playwright-core": "1.58.2" @@ -7693,7 +7647,7 @@ "version": "1.58.2", "resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/playwright-core/-/playwright-core-1.58.2.tgz", "integrity": "sha512-yZkEtftgwS8CsfYo7nm0KE8jsvm6i/PTgVtB8DL726wNf6H2IMsDuxCpJj59KDaxCtSnrWan2AeDqM7JBaultg==", - "devOptional": true, + "dev": true, "license": "Apache-2.0", "bin": { "playwright-core": "cli.js" @@ -7778,41 +7732,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/pretty-format": { - "version": "27.5.1", - "resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/pretty-format/-/pretty-format-27.5.1.tgz", - "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1", - "ansi-styles": "^5.0.0", - "react-is": "^17.0.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/pretty-format/node_modules/react-is": { - "version": "17.0.2", - "resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", - "dev": true, - "license": "MIT" - }, "node_modules/prop-types": { "version": "15.8.1", "resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/prop-types/-/prop-types-15.8.1.tgz", @@ -7860,7 +7779,6 @@ "resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/react/-/react-19.2.3.tgz", "integrity": "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==", "license": "MIT", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -7870,7 +7788,6 @@ "resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/react-dom/-/react-dom-19.2.3.tgz", "integrity": "sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==", "license": "MIT", - "peer": true, "dependencies": { "scheduler": "^0.27.0" }, @@ -7882,15 +7799,14 @@ "version": "16.13.1", "resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "license": "MIT", - "peer": true + "dev": true, + "license": "MIT" }, "node_modules/react-redux": { "version": "9.2.0", "resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/react-redux/-/react-redux-9.2.0.tgz", "integrity": "sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g==", "license": "MIT", - "peer": true, "dependencies": { "@types/use-sync-external-store": "^0.0.6", "use-sync-external-store": "^1.4.0" @@ -7957,8 +7873,7 @@ "version": "5.0.1", "resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/redux/-/redux-5.0.1.tgz", "integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/redux-thunk": { "version": "3.1.0", @@ -8938,7 +8853,6 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -9155,9 +9069,8 @@ "version": "5.9.3", "resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/typescript/-/typescript-5.9.3.tgz", "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", - "devOptional": true, + "dev": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -9351,7 +9264,6 @@ "integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", @@ -9445,7 +9357,6 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -9844,7 +9755,6 @@ "resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/zod/-/zod-4.3.6.tgz", "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", "license": "MIT", - "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } diff --git a/web/package.json b/web/package.json index b6141cf..2908bc8 100644 --- a/web/package.json +++ b/web/package.json @@ -14,7 +14,9 @@ "test:e2e:ui": "playwright test --ui" }, "dependencies": { + "@bytelyst/api-client": "file:../../learning_ai_common_plat/packages/api-client", "@bytelyst/auth-client": "file:../../learning_ai_common_plat/packages/auth-client", + "@bytelyst/react-auth": "file:../../learning_ai_common_plat/packages/react-auth", "@bytelyst/telemetry-client": "file:../../learning_ai_common_plat/packages/telemetry-client", "@serwist/next": "^9.5.6", "date-fns": "^4.1.0", diff --git a/web/src/lib/auth-context.tsx b/web/src/lib/auth-context.tsx index d342108..e9d6a00 100644 --- a/web/src/lib/auth-context.tsx +++ b/web/src/lib/auth-context.tsx @@ -1,211 +1,73 @@ // ── Auth Context ────────────────────────────────────────────── // Provides authentication state and actions for ChronoMind web. -// Calls platform-service /auth/* endpoints for login/register/me. +// Delegates to @bytelyst/react-auth shared package. 'use client'; -import { createContext, useContext, useCallback, useEffect, useState, type ReactNode } from 'react'; -import { - loginUser, - registerUser, - getMe, - setAuthToken, - setRefreshToken, - setSyncEnabled, - isAuthenticated as checkAuth, - refreshAccessToken, - forgotPassword as apiForgotPassword, - resetPassword as apiResetPassword, - changePassword as apiChangePassword, - deleteAccount as apiDeleteAccount, - type AuthUser, -} from './platform-sync'; +import { createAuthProvider } from '@bytelyst/react-auth'; +import { setSyncEnabled } from './platform-sync'; +import { PRODUCT_ID, getAuthClient } from './auth-api'; -interface AuthState { - user: AuthUser | null; - isLoading: boolean; - isAuthenticated: boolean; - error: string | null; +interface ChronoMindUser { + id: string; + email: string; + name: string; + displayName: string; + role: string; + plan: string; + [key: string]: unknown; } -interface AuthActions { - login: (email: string, password: string) => Promise; - register: (email: string, password: string, displayName: string) => Promise; - logout: () => void; - clearError: () => void; - forgotPassword: (email: string) => Promise; - resetPassword: (token: string, newPassword: string) => Promise; - changePassword: (currentPassword: string, newPassword: string) => Promise; - deleteAccount: (password: string) => Promise; - successMessage: string | null; +function getBaseUrl(): string { + if (typeof window !== 'undefined' && (window as unknown as Record).__PLATFORM_URL__) { + return (window as unknown as Record).__PLATFORM_URL__ as string; + } + return process.env.NEXT_PUBLIC_PLATFORM_SERVICE_URL ?? 'https://api.chronomind.app'; } -type AuthContextValue = AuthState & AuthActions; +const { AuthProvider: _AuthProvider, useAuth: _useAuth } = createAuthProvider({ + baseUrl: getBaseUrl(), + storagePrefix: 'chronomind', + loginEndpoint: '/auth/login', + registerEndpoint: '/auth/register', + forgotPasswordEndpoint: '/auth/forgot-password', + changePasswordEndpoint: '/auth/change-password', + deleteAccountEndpoint: '/auth/account', + refreshEndpoint: '/auth/refresh', + mapLoginResponse: (data: unknown) => { + const d = data as { user: { id: string; email: string; displayName: string; role: string; plan: string }; accessToken: string; refreshToken: string }; + setSyncEnabled(true); + return { + user: { id: d.user.id, email: d.user.email, name: d.user.displayName, displayName: d.user.displayName, role: d.user.role, plan: d.user.plan }, + accessToken: d.accessToken, + refreshToken: d.refreshToken, + }; + }, + onLogout: () => setSyncEnabled(false), +}); -const AuthContext = createContext(null); +export const AuthProvider = _AuthProvider; -export function AuthProvider({ children }: { children: ReactNode }) { - const [user, setUser] = useState(null); - const [isLoading, setIsLoading] = useState(true); - const [error, setError] = useState(null); - const [successMessage, setSuccessMessage] = useState(null); - - // Hydrate user from stored token on mount - useEffect(() => { - if (!checkAuth()) { - setIsLoading(false); - return; - } - getMe() - .then((u) => setUser(u)) - .catch(() => { - setAuthToken(null); - }) - .finally(() => setIsLoading(false)); - }, []); - - // Auto-refresh token every 45 minutes - useEffect(() => { - if (!user) return; - const REFRESH_INTERVAL = 45 * 60 * 1000; - const timer = setInterval(async () => { - const refreshed = await refreshAccessToken(); - if (!refreshed) { - setUser(null); - } - }, REFRESH_INTERVAL); - return () => clearInterval(timer); - }, [user]); - - const login = useCallback(async (email: string, password: string): Promise => { - setError(null); - try { - const result = await loginUser(email, password); - setAuthToken(result.accessToken); - setRefreshToken(result.refreshToken); - setSyncEnabled(true); - setUser(result.user); - return true; - } catch (err) { - setError(err instanceof Error ? err.message : 'Login failed'); - return false; - } - }, []); - - const register = useCallback( - async (email: string, password: string, displayName: string): Promise => { - setError(null); +/** + * Wrapper around shared useAuth that adapts naming for backward compat: + * - success → successMessage + * - clearMessages → clearError + */ +export function useAuth() { + const ctx = _useAuth(); + return { + ...ctx, + successMessage: ctx.success, + clearError: ctx.clearMessages, + resetPassword: async (token: string, newPassword: string): Promise => { try { - const result = await registerUser(email, password, displayName); - setAuthToken(result.accessToken); - setRefreshToken(result.refreshToken); - setSyncEnabled(true); - setUser(result.user); + await getAuthClient().resetPassword(token, newPassword); return true; - } catch (err) { - setError(err instanceof Error ? err.message : 'Registration failed'); + } catch { return false; } }, - [] - ); - - const logout = useCallback(() => { - setAuthToken(null); - setSyncEnabled(false); - setUser(null); - }, []); - - const clearError = useCallback(() => { - setError(null); - setSuccessMessage(null); - }, []); - - const forgotPassword = useCallback(async (email: string): Promise => { - setError(null); - setSuccessMessage(null); - try { - const result = await apiForgotPassword(email); - setSuccessMessage(result.message); - return true; - } catch (err) { - setError(err instanceof Error ? err.message : 'Failed to send reset email'); - return false; - } - }, []); - - const resetPassword = useCallback(async (token: string, newPassword: string): Promise => { - setError(null); - setSuccessMessage(null); - try { - const result = await apiResetPassword(token, newPassword); - setSuccessMessage(result.message); - return true; - } catch (err) { - setError(err instanceof Error ? err.message : 'Failed to reset password'); - return false; - } - }, []); - - const changePassword = useCallback( - async (currentPassword: string, newPassword: string): Promise => { - setError(null); - setSuccessMessage(null); - try { - const result = await apiChangePassword(currentPassword, newPassword); - setSuccessMessage(result.message); - return true; - } catch (err) { - setError(err instanceof Error ? err.message : 'Failed to change password'); - return false; - } - }, - [] - ); - - const deleteAccount = useCallback( - async (password: string): Promise => { - setError(null); - try { - await apiDeleteAccount(password); - setAuthToken(null); - setSyncEnabled(false); - setUser(null); - setSuccessMessage('Account deleted successfully.'); - return true; - } catch (err) { - setError(err instanceof Error ? err.message : 'Failed to delete account'); - return false; - } - }, - [] - ); - - return ( - - {children} - - ); + }; } -export function useAuth(): AuthContextValue { - const ctx = useContext(AuthContext); - if (!ctx) throw new Error('useAuth must be used within AuthProvider'); - return ctx; -} +export type { ChronoMindUser as AuthUser }; diff --git a/web/src/lib/platform-sync.ts b/web/src/lib/platform-sync.ts index f215642..226d786 100644 --- a/web/src/lib/platform-sync.ts +++ b/web/src/lib/platform-sync.ts @@ -4,7 +4,6 @@ import type { Timer } from './timer-engine'; import { getAuthClient, PRODUCT_ID as _PRODUCT_ID } from './auth-api'; -import type { AuthUser, AuthResult } from '@bytelyst/auth-client'; // ── DTOs ────────────────────────────────────────────────────── @@ -223,45 +222,13 @@ function setLastSyncDate(date: string): void { } // ── Auth Operations (delegated to @bytelyst/auth-client) ───── - -export type { AuthUser, AuthResult }; - -export async function loginUser(email: string, password: string): Promise { - return getAuthClient().login(email, password); -} - -export async function registerUser(email: string, password: string, displayName: string): Promise { - return getAuthClient().register(email, password, displayName); -} - -export async function getMe(): Promise { - return getAuthClient().getMe(); -} - -export async function forgotPassword(email: string): Promise<{ message: string }> { - return getAuthClient().forgotPassword(email); -} - -export async function resetPassword(token: string, newPassword: string): Promise<{ message: string }> { - return getAuthClient().resetPassword(token, newPassword); -} - -export async function changePassword(currentPassword: string, newPassword: string): Promise<{ message: string }> { - return getAuthClient().changePassword(currentPassword, newPassword); -} +// Most auth operations are now handled by @bytelyst/react-auth in auth-context.tsx. +// Only verifyEmail is still used directly by the verify-email page. export async function verifyEmail(token: string): Promise<{ message: string }> { return getAuthClient().verifyEmail(token); } -export async function resendVerification(email: string): Promise<{ message: string }> { - return getAuthClient().resendVerification(email); -} - -export async function deleteAccount(password: string): Promise<{ message: string }> { - return getAuthClient().deleteAccount(password); -} - // ── Sync Operations ─────────────────────────────────────────── export async function pullDelta(since?: string): Promise {