mirror of
https://github.com/ourongxing/newsnow.git
synced 2025-01-19 03:09:14 +08:00
fix: user verify
This commit is contained in:
parent
7030f3dec0
commit
9eeee30e8c
@ -63,8 +63,8 @@ export default defineEventHandler(async (event) => {
|
||||
|
||||
const params = new URLSearchParams({
|
||||
login: "github",
|
||||
user_jwt: jwtToken,
|
||||
user_info: JSON.stringify({
|
||||
jwt: jwtToken,
|
||||
user: JSON.stringify({
|
||||
avatar: userInfo.avatar_url,
|
||||
name: userInfo.name,
|
||||
}),
|
||||
|
@ -6,13 +6,13 @@ export default defineEventHandler(async (event) => {
|
||||
console.log(url.pathname)
|
||||
if (["JWT_SECRET", "G_CLIENT_ID", "G_CLIENT_SECRET"].find(k => !process.env[k])) {
|
||||
event.context.disabledLogin = true
|
||||
if (url.pathname.startsWith("/me")) throw createError({ statusCode: 506, message: "Server not configured" })
|
||||
if (url.pathname.startsWith("/api/me")) throw createError({ statusCode: 506, message: "Server not configured" })
|
||||
} else {
|
||||
if (/^\/api\/(?:me|s)\//.test(url.pathname)) {
|
||||
const token = getHeader(event, "Authorization")
|
||||
const token = getHeader(event, "Authorization")?.replace("Bearer ", "")?.trim()
|
||||
if (token && process.env.JWT_SECRET) {
|
||||
try {
|
||||
const { payload } = await jwtVerify(token.replace("Bearer ", ""), new TextEncoder().encode(process.env.JWT_SECRET)) as { payload?: { id: string, type: string } }
|
||||
const { payload } = await jwtVerify(token, new TextEncoder().encode(process.env.JWT_SECRET)) as { payload?: { id: string, type: string } }
|
||||
if (payload?.id) {
|
||||
event.context.user = {
|
||||
id: payload.id,
|
||||
@ -20,7 +20,7 @@ export default defineEventHandler(async (event) => {
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
if (url.pathname.startsWith("/me")) throw createError({ statusCode: 401, message: "JWT verification failed" })
|
||||
if (url.pathname.startsWith("/api/me")) throw createError({ statusCode: 401, message: "JWT verification failed" })
|
||||
logger.warn("JWT verification failed")
|
||||
}
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ import { ofetch } from "ofetch"
|
||||
import { OverlayScrollbar } from "../common/overlay-scrollbar"
|
||||
import { focusSourcesAtom, refetchSourcesAtom } from "~/atoms"
|
||||
import { useRelativeTime } from "~/hooks/useRelativeTime"
|
||||
import { safeParseString } from "~/utils"
|
||||
|
||||
export interface ItemsProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||
id: SourceID
|
||||
@ -71,7 +72,7 @@ function NewsCard({ id, inView, handleListeners }: NewsCardProps) {
|
||||
const response: SourceResponse = await ofetch(url, {
|
||||
timeout: 10000,
|
||||
headers: {
|
||||
Authorization: `Bearer ${localStorage.getItem("user_jwt")}`,
|
||||
Authorization: `Bearer ${safeParseString(localStorage.getItem("jwt"))}`,
|
||||
},
|
||||
})
|
||||
return response
|
||||
|
27
src/hooks/useLogin.ts
Normal file
27
src/hooks/useLogin.ts
Normal file
@ -0,0 +1,27 @@
|
||||
import { useAtom } from "jotai"
|
||||
import { atomWithStorage } from "jotai/utils"
|
||||
import { useCallback } from "react"
|
||||
|
||||
const userAtom = atomWithStorage<{
|
||||
name?: string
|
||||
avatar?: string
|
||||
}>("user", {})
|
||||
|
||||
const jwtAtom = atomWithStorage("jwt", "")
|
||||
|
||||
export function useLogin() {
|
||||
const [userInfo] = useAtom(userAtom)
|
||||
const [jwt, setJwt] = useAtom(jwtAtom)
|
||||
const enabledLogin = __ENABLE_LOGIN__
|
||||
const login = useCallback(() => {
|
||||
window.location.href = `https://github.com/login/oauth/authorize?client_id=${__G_CLIENT_ID__}`
|
||||
}, [])
|
||||
|
||||
return {
|
||||
enabledLogin,
|
||||
loggedIn: !!jwt,
|
||||
userInfo,
|
||||
logout: () => setJwt(""),
|
||||
login,
|
||||
}
|
||||
}
|
@ -1,13 +1,15 @@
|
||||
import type { PrimitiveMetadata } from "@shared/types"
|
||||
import { useAtom } from "jotai"
|
||||
import { ofetch } from "ofetch"
|
||||
import { useEffect } from "react"
|
||||
import { useDebounce } from "react-use"
|
||||
import { useDebounce, useMount } from "react-use"
|
||||
import { toast } from "sonner"
|
||||
import { useLogin } from "./useLogin"
|
||||
import { preprocessMetadata, primitiveMetadataAtom } from "~/atoms"
|
||||
import { safeParseString } from "~/utils"
|
||||
|
||||
export async function uploadMetadata(metadata: PrimitiveMetadata) {
|
||||
if (!__ENABLE_LOGIN__) return
|
||||
const jwt = localStorage.getItem("user_jwt")
|
||||
const jwt = localStorage.getItem("jwt")
|
||||
if (!jwt) return
|
||||
try {
|
||||
await ofetch("/api/me/sync", {
|
||||
@ -27,42 +29,51 @@ export async function uploadMetadata(metadata: PrimitiveMetadata) {
|
||||
|
||||
export async function downloadMetadata(): Promise<PrimitiveMetadata | undefined> {
|
||||
if (!__ENABLE_LOGIN__) return
|
||||
const jwt = localStorage.getItem("user_jwt")
|
||||
const jwt = safeParseString(localStorage.getItem("jwt"))
|
||||
if (!jwt) return
|
||||
try {
|
||||
const { data, updatedTime } = await ofetch("/api/me/sync", {
|
||||
headers: {
|
||||
Authorization: `Bearer ${jwt}`,
|
||||
},
|
||||
}) as PrimitiveMetadata
|
||||
// 不用同步 action 字段
|
||||
if (data) {
|
||||
return {
|
||||
action: "sync",
|
||||
data,
|
||||
updatedTime,
|
||||
}
|
||||
const { data, updatedTime } = await ofetch("/api/me/sync", {
|
||||
headers: {
|
||||
Authorization: `Bearer ${jwt}`,
|
||||
},
|
||||
}) as PrimitiveMetadata
|
||||
// 不用同步 action 字段
|
||||
if (data) {
|
||||
return {
|
||||
action: "sync",
|
||||
data,
|
||||
updatedTime,
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
}
|
||||
|
||||
export function useSync() {
|
||||
const [primitiveMetadata, setPrimitiveMetadata] = useAtom(primitiveMetadataAtom)
|
||||
const { logout, login } = useLogin()
|
||||
|
||||
useDebounce(async () => {
|
||||
if (primitiveMetadata.action === "manual") {
|
||||
uploadMetadata(primitiveMetadata)
|
||||
}
|
||||
}, 10000, [primitiveMetadata])
|
||||
useEffect(() => {
|
||||
useMount(() => {
|
||||
const fn = async () => {
|
||||
const metadata = await downloadMetadata()
|
||||
if (metadata) {
|
||||
setPrimitiveMetadata(preprocessMetadata(metadata))
|
||||
try {
|
||||
const metadata = await downloadMetadata()
|
||||
if (metadata) {
|
||||
setPrimitiveMetadata(preprocessMetadata(metadata))
|
||||
}
|
||||
} catch (e: any) {
|
||||
if (e.statusCode === 401) {
|
||||
toast.error("身份校验失败,请重新登录", {
|
||||
action: {
|
||||
label: "登录",
|
||||
onClick: login,
|
||||
},
|
||||
})
|
||||
logout()
|
||||
}
|
||||
}
|
||||
}
|
||||
fn()
|
||||
}, [setPrimitiveMetadata])
|
||||
})
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ import { useOnReload } from "~/hooks/useOnReload"
|
||||
import { GlobalOverlayScrollbar } from "~/components/common/overlay-scrollbar"
|
||||
import { useSync } from "~/hooks/useSync"
|
||||
import { Footer } from "~/components/footer"
|
||||
import { Toast } from "~/components/common/toast"
|
||||
|
||||
export const Route = createRootRouteWithContext<{
|
||||
queryClient: QueryClient
|
||||
@ -18,8 +19,9 @@ export const Route = createRootRouteWithContext<{
|
||||
notFoundComponent: NotFoundComponent,
|
||||
beforeLoad: () => {
|
||||
const query = new URLSearchParams(window.location.search)
|
||||
if (query.has("login")) {
|
||||
[...query.entries()].forEach(key => localStorage.setItem(key[0], key[1]))
|
||||
if (query.has("login") && query.has("user") && query.has("jwt")) {
|
||||
localStorage.setItem("user", query.get("user")!)
|
||||
localStorage.setItem("jwt", JSON.stringify(query.get("jwt")!))
|
||||
window.history.replaceState({}, document.title, window.location.pathname)
|
||||
}
|
||||
},
|
||||
@ -46,15 +48,15 @@ function RootComponent() {
|
||||
<header className={clsx([
|
||||
"flex justify-between items-center py-4 px-5",
|
||||
"lg:(py-6)",
|
||||
"sticky top-0 z-100 backdrop-blur-md",
|
||||
"sticky top-0 z-10 backdrop-blur-md",
|
||||
])}
|
||||
>
|
||||
<Header />
|
||||
</header>
|
||||
<main className={clsx([
|
||||
"min-h-[calc(100vh-170px)] transition-margin",
|
||||
"md:(min-h-[calc(100vh-110px)] mt--14)",
|
||||
"lg:(min-h-[calc(100vh-124px)] mt--16)",
|
||||
"min-h-[calc(100vh-170px)]",
|
||||
"md:(min-h-[calc(100vh-110px)])",
|
||||
"lg:(min-h-[calc(100vh-124px)])",
|
||||
])}
|
||||
>
|
||||
<Outlet />
|
||||
@ -63,6 +65,7 @@ function RootComponent() {
|
||||
<Footer />
|
||||
</footer>
|
||||
</GlobalOverlayScrollbar>
|
||||
<Toast />
|
||||
{import.meta.env.DEV && (
|
||||
<>
|
||||
<ReactQueryDevtools buttonPosition="bottom-left" />
|
||||
|
7
src/utils/index.ts
Normal file
7
src/utils/index.ts
Normal file
@ -0,0 +1,7 @@
|
||||
export function safeParseString(str: any) {
|
||||
try {
|
||||
return JSON.parse(str)
|
||||
} catch {
|
||||
return ""
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user