From a4b902893ee47a97e6eda007f5687b4a79bb1116 Mon Sep 17 00:00:00 2001 From: chenhang Date: Wed, 10 Jun 2026 10:26:45 +0800 Subject: [PATCH] refactor(auth): migrate social login to next-auth --- package.json | 3 +- pnpm-lock.yaml | 147 +++++++++-- src/app/api/auth/[...nextauth]/route.ts | 19 ++ .../auth/components/auth-facebook-panel.tsx | 54 ++-- src/app/auth/components/auth-panel.tsx | 7 +- src/app/auth/components/auth-screen.tsx | 10 +- .../auth/components/google-sign-in-button.tsx | 50 ---- src/app/auth/components/index.ts | 1 - src/app/layout.tsx | 19 +- src/app/splash/components/splash-button.tsx | 29 ++- .../services/auth/auth_bridge_component.tsx | 242 ------------------ src/data/services/auth/auth_event_bridge.ts | 47 ---- src/data/services/auth/auth_platform.ts | 91 ------- src/data/services/auth/global.d.ts | 50 ---- src/data/services/auth/index.ts | 12 - .../services/auth/initialize_facebook_auth.ts | 27 -- .../services/auth/initialize_google_auth.ts | 23 -- src/data/services/auth/mock_auth_platform.ts | 62 ----- src/data/services/auth/web_auth_platform.ts | 195 -------------- src/data/services/index.ts | 9 +- src/integrations/facebook-sdk.d.ts | 39 --- src/integrations/facebook-sdk.ts | 147 ----------- src/integrations/google-identity.d.ts | 36 --- src/integrations/google-identity.ts | 72 ------ src/integrations/index.ts | 4 - src/lib/auth/config.ts | 129 ++++++++++ src/lib/auth/nextauth-helpers.ts | 181 +++++++++++++ src/lib/auth/nextauth.ts | 27 ++ src/lib/auth/route-guards.ts | 36 --- src/lib/auth/use-auth-gate.tsx | 56 ---- src/lib/index.ts | 6 +- src/proxy.ts | 48 ++-- src/stores/auth/auth-context.tsx | 5 +- src/stores/auth/auth-machine.ts | 110 +------- 34 files changed, 572 insertions(+), 1421 deletions(-) create mode 100644 src/app/api/auth/[...nextauth]/route.ts delete mode 100644 src/app/auth/components/google-sign-in-button.tsx delete mode 100644 src/data/services/auth/auth_bridge_component.tsx delete mode 100644 src/data/services/auth/auth_event_bridge.ts delete mode 100644 src/data/services/auth/auth_platform.ts delete mode 100644 src/data/services/auth/global.d.ts delete mode 100644 src/data/services/auth/index.ts delete mode 100644 src/data/services/auth/initialize_facebook_auth.ts delete mode 100644 src/data/services/auth/initialize_google_auth.ts delete mode 100644 src/data/services/auth/mock_auth_platform.ts delete mode 100644 src/data/services/auth/web_auth_platform.ts delete mode 100644 src/integrations/facebook-sdk.d.ts delete mode 100644 src/integrations/facebook-sdk.ts delete mode 100644 src/integrations/google-identity.d.ts delete mode 100644 src/integrations/google-identity.ts create mode 100644 src/lib/auth/config.ts create mode 100644 src/lib/auth/nextauth-helpers.ts create mode 100644 src/lib/auth/nextauth.ts delete mode 100644 src/lib/auth/route-guards.ts delete mode 100644 src/lib/auth/use-auth-gate.tsx diff --git a/package.json b/package.json index 24a98e57..3eb25578 100644 --- a/package.json +++ b/package.json @@ -15,13 +15,12 @@ }, "dependencies": { "@fingerprintjs/fingerprintjs": "^5.2.0", - "@greatsumini/react-facebook-login": "^3.4.0", - "@react-oauth/google": "^0.13.5", "@xstate/react": "^5", "classnames": "^2.5.1", "dexie": "^4.4.3", "lottie-react": "^2.4.1", "next": "16.2.7", + "next-auth": "^4.24.14", "ofetch": "^1.5.1", "react": "19.2.4", "react-dom": "19.2.4", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3dd612ba..23b8e5ec 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11,12 +11,6 @@ importers: '@fingerprintjs/fingerprintjs': specifier: ^5.2.0 version: 5.2.0 - '@greatsumini/react-facebook-login': - specifier: ^3.4.0 - version: 3.4.0(react@19.2.4) - '@react-oauth/google': - specifier: ^0.13.5 - version: 0.13.5(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@xstate/react': specifier: ^5 version: 5.0.5(@types/react@19.2.16)(react@19.2.4)(xstate@5.32.0) @@ -32,6 +26,9 @@ importers: next: specifier: 16.2.7 version: 16.2.7(@babel/core@7.29.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + next-auth: + specifier: ^4.24.14 + version: 4.24.14(next@16.2.7(@babel/core@7.29.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react-dom@19.2.4(react@19.2.4))(react@19.2.4) ofetch: specifier: ^1.5.1 version: 1.5.1 @@ -167,6 +164,10 @@ packages: engines: {node: '>=6.0.0'} hasBin: true + '@babel/runtime@7.29.7': + resolution: {integrity: sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==} + engines: {node: '>=6.9.0'} + '@babel/template@7.29.7': resolution: {integrity: sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==} engines: {node: '>=6.9.0'} @@ -282,11 +283,6 @@ packages: '@fingerprintjs/fingerprintjs@5.2.0': resolution: {integrity: sha512-j+2nInkwCQNTJcNhOjvkGM/nLRTuGJTC6xai4quqvUpjob2ssrGwBZjS7k55nOmKvge7qvJT2nS3i/IRvQSTQA==} - '@greatsumini/react-facebook-login@3.4.0': - resolution: {integrity: sha512-NJYqTOcDghZ6zLppC+LJVLep7Xi3gfx9ssQFRd7EhDTU07lC15SG/eZ3ZyvUPsuPBkbhVl6E/Py0w4M5m7Lh9Q==} - peerDependencies: - react: ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^20.0.0 || ^21.0.0 - '@humanfs/core@0.19.2': resolution: {integrity: sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==} engines: {node: '>=18.18.0'} @@ -559,15 +555,12 @@ packages: '@oxc-project/types@0.133.0': resolution: {integrity: sha512-KzkdCd6Uxqnf6l3HOw1xfatAlUURA0g14cvBYFyJ5SaNOQbOUvBr9PKArcPcrNIeRsBdgcUzOGrhKveVpvOIGA==} + '@panva/hkdf@1.2.1': + resolution: {integrity: sha512-6oclG6Y3PiDFcoyk8srjLfVKyMfVCKJ27JwNPViuXziFpmdz+MZnZN/aKY0JGXgYuO/VghU0jcOAZgWXZ1Dmrw==} + '@polka/url@1.0.0-next.29': resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==} - '@react-oauth/google@0.13.5': - resolution: {integrity: sha512-xQWri2s/3nNekZJ4uuov2aAfQYu83bN3864KcFqw2pK1nNbFurQIjPFDXhWaKH3IjYJ2r/9yyIIpsn5lMqrheQ==} - peerDependencies: - react: '>=16.8.0' - react-dom: '>=16.8.0' - '@rolldown/binding-android-arm64@1.0.3': resolution: {integrity: sha512-454rs7jHngixp/NMxd5srYD57OnzSlZ/eFTETjORQHLwJG1lRtmNOJcBerZlfu4GjKqeq8aCCIQrMdHyhI51Hw==} engines: {node: ^20.19.0 || >=22.12.0} @@ -1230,6 +1223,10 @@ packages: cookie-es@1.2.3: resolution: {integrity: sha512-lXVyvUvrNXblMqzIRrxHb57UUVmqsSWlxqt3XIjCkUP0wDAf6uicO6KMbEgYrMNtEvWgWHwe42CKxPu9MYAnWw==} + cookie@0.7.2: + resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} + engines: {node: '>= 0.6'} + cross-spawn@7.0.6: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} @@ -1846,6 +1843,9 @@ packages: resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==} hasBin: true + jose@4.15.9: + resolution: {integrity: sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA==} + js-tokens@10.0.0: resolution: {integrity: sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q==} @@ -2018,6 +2018,10 @@ packages: lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + lru-cache@6.0.0: + resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} + engines: {node: '>=10'} + magic-string@0.30.21: resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} @@ -2073,6 +2077,20 @@ packages: natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + next-auth@4.24.14: + resolution: {integrity: sha512-YRz6xFDXKUwiXSMMChbrBEWyFktZ1qZXEgeSHQQ3nsy08B4c/xLk6REeutRsIFwkjY/1+ShHnu07DN3JeJguig==} + peerDependencies: + '@auth/core': 0.34.3 + next: ^12.2.5 || ^13 || ^14 || ^15 || ^16 + nodemailer: ^7.0.7 + react: ^17.0.2 || ^18 || ^19 + react-dom: ^17.0.2 || ^18 || ^19 + peerDependenciesMeta: + '@auth/core': + optional: true + nodemailer: + optional: true + next@16.2.7: resolution: {integrity: sha512-eMJxgjRzBaj3olkP4cBamHDXL79A8FC6u1GcsO1D1Tsx8bw/LLXUJCaoajVxtnhD3A1IJqIT8IcRJjgBIPJq4w==} engines: {node: '>=20.9.0'} @@ -2112,10 +2130,17 @@ packages: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} engines: {node: '>=0.10.0'} + oauth@0.9.15: + resolution: {integrity: sha512-a5ERWK1kh38ExDEfoO6qUHJb32rd7aYmPHuyCu3Fta/cnICvYmgd2uhuKXvPD+PXB+gCEYYEaQdIRAjCOwAKNA==} + object-assign@4.1.1: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} + object-hash@2.2.0: + resolution: {integrity: sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==} + engines: {node: '>= 6'} + object-inspect@1.13.4: resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} engines: {node: '>= 0.4'} @@ -2151,6 +2176,13 @@ packages: ofetch@1.5.1: resolution: {integrity: sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA==} + oidc-token-hash@5.2.0: + resolution: {integrity: sha512-6gj2m8cJZ+iSW8bm0FXdGF0YhIQbKrfP4yWTNzxc31U6MOjfEmB1rHvlYvxI1B7t7BCi1F2vYTT6YhtQRG4hxw==} + engines: {node: ^10.13.0 || >=12.0.0} + + openid-client@5.7.1: + resolution: {integrity: sha512-jDBPgSVfTnkIh71Hg9pRvtJc6wTwqjRkN88+gCFtYWrlP4Yx2Dsrow8uPi3qLr/aeymPF3o2+dS+wOpglK04ew==} + optionator@0.9.4: resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} engines: {node: '>= 0.8.0'} @@ -2239,10 +2271,21 @@ packages: resolution: {integrity: sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==} engines: {node: ^10 || ^12 || >=14} + preact-render-to-string@5.2.6: + resolution: {integrity: sha512-JyhErpYOvBV1hEPwIxc/fHWXPfnEGdRKxc8gFdAZ7XV4tlzyzG847XAyEZqoDnynP88akM4eaHcSOzNcLWFguw==} + peerDependencies: + preact: '>=10' + + preact@10.29.2: + resolution: {integrity: sha512-7tNmwg/7mzzAoB/8kSg6Hl37JraAZw3Z3A0JSY7VXlZwo82Xn0G7wKbNNs2qoF4ZEEsQGTwDAroNdqKs1ofJxQ==} + prelude-ls@1.2.1: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} + pretty-format@3.8.0: + resolution: {integrity: sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew==} + prop-types@15.8.1: resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} @@ -2669,6 +2712,11 @@ packages: peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + uuid@8.3.2: + resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} + deprecated: uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028). + hasBin: true + vite@8.0.16: resolution: {integrity: sha512-h9bXPmJichP5fLmVQo3PyaGSDE2n3aPuomeAlVRm0JLmt4rY6zmPKd59HYI4LNW8oTK7tlTsuC7l/m7awx9Jcw==} engines: {node: ^20.19.0 || >=22.12.0} @@ -2820,6 +2868,9 @@ packages: yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + yallist@4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + yargs-parser@21.1.1: resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} engines: {node: '>=12'} @@ -2942,6 +2993,8 @@ snapshots: dependencies: '@babel/types': 7.29.7 + '@babel/runtime@7.29.7': {} + '@babel/template@7.29.7': dependencies: '@babel/code-frame': 7.29.7 @@ -3061,10 +3114,6 @@ snapshots: '@fingerprintjs/fingerprintjs@5.2.0': {} - '@greatsumini/react-facebook-login@3.4.0(react@19.2.4)': - dependencies: - react: 19.2.4 - '@humanfs/core@0.19.2': dependencies: '@humanfs/types': 0.15.0 @@ -3250,12 +3299,9 @@ snapshots: '@oxc-project/types@0.133.0': {} - '@polka/url@1.0.0-next.29': {} + '@panva/hkdf@1.2.1': {} - '@react-oauth/google@0.13.5(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': - dependencies: - react: 19.2.4 - react-dom: 19.2.4(react@19.2.4) + '@polka/url@1.0.0-next.29': {} '@rolldown/binding-android-arm64@1.0.3': optional: true @@ -3880,6 +3926,8 @@ snapshots: cookie-es@1.2.3: {} + cookie@0.7.2: {} + cross-spawn@7.0.6: dependencies: path-key: 3.1.1 @@ -4645,6 +4693,8 @@ snapshots: jiti@2.7.0: {} + jose@4.15.9: {} + js-tokens@10.0.0: {} js-tokens@4.0.0: {} @@ -4802,6 +4852,10 @@ snapshots: dependencies: yallist: 3.1.1 + lru-cache@6.0.0: + dependencies: + yallist: 4.0.0 + magic-string@0.30.21: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 @@ -4847,6 +4901,21 @@ snapshots: natural-compare@1.4.0: {} + next-auth@4.24.14(next@16.2.7(@babel/core@7.29.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react-dom@19.2.4(react@19.2.4))(react@19.2.4): + dependencies: + '@babel/runtime': 7.29.7 + '@panva/hkdf': 1.2.1 + cookie: 0.7.2 + jose: 4.15.9 + next: 16.2.7(@babel/core@7.29.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + oauth: 0.9.15 + openid-client: 5.7.1 + preact: 10.29.2 + preact-render-to-string: 5.2.6(preact@10.29.2) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + uuid: 8.3.2 + next@16.2.7(@babel/core@7.29.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4): dependencies: '@next/env': 16.2.7 @@ -4886,8 +4955,12 @@ snapshots: normalize-path@3.0.0: {} + oauth@0.9.15: {} + object-assign@4.1.1: {} + object-hash@2.2.0: {} + object-inspect@1.13.4: {} object-keys@1.1.1: {} @@ -4936,6 +5009,15 @@ snapshots: node-fetch-native: 1.6.7 ufo: 1.6.4 + oidc-token-hash@5.2.0: {} + + openid-client@5.7.1: + dependencies: + jose: 4.15.9 + lru-cache: 6.0.0 + object-hash: 2.2.0 + oidc-token-hash: 5.2.0 + optionator@0.9.4: dependencies: deep-is: 0.1.4 @@ -5019,8 +5101,17 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 + preact-render-to-string@5.2.6(preact@10.29.2): + dependencies: + preact: 10.29.2 + pretty-format: 3.8.0 + + preact@10.29.2: {} + prelude-ls@1.2.1: {} + pretty-format@3.8.0: {} + prop-types@15.8.1: dependencies: loose-envify: 1.4.0 @@ -5506,6 +5597,8 @@ snapshots: dependencies: react: 19.2.4 + uuid@8.3.2: {} + vite@8.0.16(@types/node@20.19.41)(jiti@2.7.0): dependencies: lightningcss: 1.32.0 @@ -5632,6 +5725,8 @@ snapshots: yallist@3.1.1: {} + yallist@4.0.0: {} + yargs-parser@21.1.1: {} yargs@17.7.2: diff --git a/src/app/api/auth/[...nextauth]/route.ts b/src/app/api/auth/[...nextauth]/route.ts new file mode 100644 index 00000000..d3f58509 --- /dev/null +++ b/src/app/api/auth/[...nextauth]/route.ts @@ -0,0 +1,19 @@ +/** + * NextAuth App Router 入口 + * + * 路径:/api/auth/[...nextauth] + * 自动处理 NextAuth 所有内置路由: + * /api/auth/signin/[provider] + * /api/auth/callback/[provider] + * /api/auth/signout + * /api/auth/session + * /api/auth/csrf + * /api/auth/providers + */ +import NextAuth from "next-auth"; + +import { authOptions } from "@/lib/auth/config"; + +const handler = NextAuth(authOptions); + +export { handler as GET, handler as POST }; diff --git a/src/app/auth/components/auth-facebook-panel.tsx b/src/app/auth/components/auth-facebook-panel.tsx index 6c8370ab..b02e7477 100644 --- a/src/app/auth/components/auth-facebook-panel.tsx +++ b/src/app/auth/components/auth-facebook-panel.tsx @@ -6,54 +6,54 @@ * * 行为: * - 默认显示「Continue with Facebook」+ 「Other sign-in options」入口 - * - 真实 Facebook 登录在「Continue with Facebook」按钮中触发 + * - 社交登录直接调 `nextauth.facebookLogin()` / `nextauth.googleLogin()`(NextAuth 流程) */ import { useState } from "react"; -import { useAuthState, useAuthDispatch } from "@/stores/auth/auth-context"; +import { useAuthState } from "@/stores/auth/auth-context"; +import { facebookLogin, googleLogin } from "@/lib/auth/nextauth"; import { AuthSocialButtons } from "./auth-social-buttons"; import { AuthDivider } from "./auth-divider"; import { AuthPrimaryButton } from "./auth-primary-button"; import { AuthErrorMessage } from "./auth-error-message"; import { AuthOtherOptionsDialog } from "./auth-other-options-dialog"; -import { GoogleSignInButton } from "./google-sign-in-button"; import { AuthLegalText } from "./auth-legal-text"; import styles from "./auth-facebook-panel.module.css"; export interface AuthFacebookPanelProps { onSwitchToEmail: () => void; - onGoogleSuccess: (idToken: string, email: string) => void; } -export function AuthFacebookPanel({ - onSwitchToEmail, - onGoogleSuccess, -}: AuthFacebookPanelProps) { +export function AuthFacebookPanel({ onSwitchToEmail }: AuthFacebookPanelProps) { const state = useAuthState(); - const dispatch = useAuthDispatch(); const [showOptions, setShowOptions] = useState(false); + const [busy, setBusy] = useState(null); + + const handleFacebook = async () => { + setBusy("facebook"); + const r = await facebookLogin(); + setBusy(null); + if (!r.success) console.error("[auth-facebook-panel]", r.error); + }; + + const handleGoogle = async () => { + setBusy("google"); + const r = await googleLogin(); + setBusy(null); + if (!r.success) console.error("[auth-facebook-panel]", r.error); + }; return (
dispatch({ type: "AuthFacebookLoginSubmitted" })} - onGoogle={() => dispatch({ type: "AuthGoogleLoginSubmitted" })} - googleSlot={ - - onGoogleSuccess(s.idToken, s.email) - } - /> - } + loadingProvider={busy ?? (state.isLoading ? "facebook" : null)} + onFacebook={handleFacebook} + onGoogle={handleGoogle} /> - + Continue with Email
diff --git a/src/app/auth/components/auth-panel.tsx b/src/app/auth/components/auth-panel.tsx index 246488b9..58a7d372 100644 --- a/src/app/auth/components/auth-panel.tsx +++ b/src/app/auth/components/auth-panel.tsx @@ -10,11 +10,7 @@ import { AuthFacebookPanel } from "./auth-facebook-panel"; import { AuthEmailPanel } from "./auth-email-panel"; import styles from "./auth-panel.module.css"; -export interface AuthPanelProps { - onGoogleSuccess: (idToken: string, email: string) => void; -} - -export function AuthPanel({ onGoogleSuccess }: AuthPanelProps) { +export function AuthPanel() { const state = useAuthState(); return (
@@ -26,7 +22,6 @@ export function AuthPanel({ onGoogleSuccess }: AuthPanelProps) { // 但我们目前 AuthFacebookPanel 内部 click "Continue with Email" 直接调用 onSwitchToEmail // 实际上本组件直接渲染对应 panel }} - onGoogleSuccess={onGoogleSuccess} /> ) : ( undefined} /> diff --git a/src/app/auth/components/auth-screen.tsx b/src/app/auth/components/auth-screen.tsx index c3055c5e..cba65af7 100644 --- a/src/app/auth/components/auth-screen.tsx +++ b/src/app/auth/components/auth-screen.tsx @@ -16,7 +16,7 @@ import { useRouter } from "next/navigation"; import { useAuthState, useAuthDispatch } from "@/stores/auth/auth-context"; import { useChatDispatch } from "@/stores/chat/chat-context"; import { useUserDispatch } from "@/stores/user/user-context"; -import { useAuthGate } from "@/lib/auth/use-auth-gate"; +import { useAuthGate } from "@/lib/auth/nextauth"; import { ROUTES } from "@/lib/routes"; import { MobileShell } from "@/app/_components/core/mobile-shell"; import { AuthPanel } from "@/app/auth/components/auth-panel"; @@ -42,7 +42,7 @@ export function AuthScreen({ if (isAuthed) router.replace(ROUTES.chat); }, [isAuthed, router]); - // 登录成功 → 通知 Chat + User + 跳转 + // 邮箱登录成功 → 通知 Chat + User + 跳转 useEffect(() => { if (state.isSuccess && !wasSuccess.current) { wasSuccess.current = true; @@ -63,11 +63,7 @@ export function AuthScreen({ padding: "var(--spacing-lg)", }} > - { - authDispatch({ type: "AuthWebGoogleLoginSuccess", idToken, email }); - }} - /> +
); diff --git a/src/app/auth/components/google-sign-in-button.tsx b/src/app/auth/components/google-sign-in-button.tsx deleted file mode 100644 index b0598b6c..00000000 --- a/src/app/auth/components/google-sign-in-button.tsx +++ /dev/null @@ -1,50 +0,0 @@ -"use client"; -/** - * Google 登录按钮(GIS 渲染) - * - * 原始 Dart: lib/ui/auth/widgets/google_sign_in_web.dart - * - * 通过 `google.accounts.id.renderButton` 把 GIS 官方按钮渲染到指定 div。 - * 登录成功由 `setOnGoogleSignInSuccess` 设置的回调处理。 - */ -import { useEffect, useRef } from "react"; - -import { - renderGoogleButton, - setOnGoogleSignInSuccess, - type GoogleSignInSuccess, -} from "@/integrations/google-identity"; -import styles from "./google-sign-in-button.module.css"; - -export interface GoogleSignInButtonProps { - onSuccess: (success: GoogleSignInSuccess) => void; -} - -export function GoogleSignInButton({ onSuccess }: GoogleSignInButtonProps) { - const ref = useRef(null); - - useEffect(() => { - setOnGoogleSignInSuccess(onSuccess); - return () => setOnGoogleSignInSuccess(null); - }, [onSuccess]); - - useEffect(() => { - if (!ref.current) return; - let cancelled = false; - // GIS 异步加载,轮询直到可用 - const tryRender = () => { - if (cancelled) return; - if (window.google?.accounts?.id) { - renderGoogleButton(ref.current!, { theme: "outline", size: "large" }); - } else { - setTimeout(tryRender, 100); - } - }; - tryRender(); - return () => { - cancelled = true; - }; - }, []); - - return
; -} diff --git a/src/app/auth/components/index.ts b/src/app/auth/components/index.ts index 782a9982..89b6b7cd 100644 --- a/src/app/auth/components/index.ts +++ b/src/app/auth/components/index.ts @@ -17,5 +17,4 @@ export * from "./auth-text-field"; export * from "./auth-validators"; export * from "./email-login-form"; export * from "./email-register-form"; -export * from "./google-sign-in-button"; export * from "./__tests__/auth-validators.test"; diff --git a/src/app/layout.tsx b/src/app/layout.tsx index d107fb14..e3b0ed8c 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -1,7 +1,7 @@ import type { Metadata } from "next"; import { Geist, Geist_Mono } from "next/font/google"; import localFont from "next/font/local"; -import Script from "next/script"; +import { SessionProvider } from "next-auth/react"; import "./globals.css"; import { RootProviders } from "@/providers/root-providers"; @@ -61,24 +61,17 @@ export default function RootLayout({ }>) { return ( // suppressHydrationWarning:next/font + Tailwind v4 偶发类名差异;详见 - // `src/lib/auth/use-auth-gate.tsx` 的"水合闪屏"说明。 + // `src/lib/auth/nextauth.ts` 的"水合闪屏"说明。 - {/* Facebook SDK JS(auth + splash 入口会用到 FB.login) */} -