feat: refactor login

This commit is contained in:
Ou 2024-11-03 20:13:49 +08:00
parent 75ab32d250
commit 773ab138ca
12 changed files with 61 additions and 37 deletions

View File

@ -0,0 +1,8 @@
import process from "node:process"
export default defineEventHandler(async () => {
return {
enable: true,
url: `https://github.com/login/oauth/authorize?client_id=${process.env.G_CLIENT_ID}`,
}
})

View File

@ -53,10 +53,10 @@ function NewsCard({ id, handleListeners }: NewsCardProps) {
queryKey: [id, getRefreshId(id)], queryKey: [id, getRefreshId(id)],
queryFn: async ({ queryKey }) => { queryFn: async ({ queryKey }) => {
const [_id, _refetchTime] = queryKey as [SourceID, number] const [_id, _refetchTime] = queryKey as [SourceID, number]
let url = `/api/s?id=${_id}` let url = `/s?id=${_id}`
const headers: Record<string, any> = {} const headers: Record<string, any> = {}
if (Date.now() - _refetchTime < 1000) { if (Date.now() - _refetchTime < 1000) {
url = `/api/s?id=${_id}&latest` url = `/s?id=${_id}&latest`
const jwt = safeParseString(localStorage.getItem("jwt")) const jwt = safeParseString(localStorage.getItem("jwt"))
if (jwt) headers.Authorization = `Bearer ${jwt}` if (jwt) headers.Authorization = `Bearer ${jwt}`
} else if (cache.has(_id)) { } else if (cache.has(_id)) {

View File

@ -25,10 +25,11 @@ import { currentSourcesAtom } from "~/atoms"
export function Dnd() { export function Dnd() {
const [items, setItems] = useAtom(currentSourcesAtom) const [items, setItems] = useAtom(currentSourcesAtom)
useQuery({ useQuery({
queryKey: ["entries", items.sort()], // sort in place
queryKey: ["entries", [...items].sort()],
queryFn: async ({ queryKey }) => { queryFn: async ({ queryKey }) => {
const sources = queryKey[1] const sources = queryKey[1]
const res: EntriesSourceResponse = await myFetch("/api/s/entries", { const res: EntriesSourceResponse = await myFetch("/s/entries", {
method: "POST", method: "POST",
body: { body: {
sources, sources,

View File

@ -14,7 +14,7 @@ function ThemeToggle() {
} }
export function Menu() { export function Menu() {
const { loggedIn, login, logout, userInfo } = useLogin() const { loggedIn, login, logout, userInfo, enableLogin } = useLogin()
const [shown, show] = useState(false) const [shown, show] = useState(false)
const ref = useRef<HTMLElement>(null) const ref = useRef<HTMLElement>(null)
const isHover = useHoverDirty(ref) const isHover = useHoverDirty(ref)
@ -25,7 +25,7 @@ export function Menu() {
<span ref={ref} className="relative"> <span ref={ref} className="relative">
<span className="flex items-center scale-90"> <span className="flex items-center scale-90">
{ {
loggedIn && userInfo.avatar enableLogin && loggedIn && userInfo.avatar
? ( ? (
<button <button
type="button" type="button"
@ -57,7 +57,7 @@ export function Menu() {
}} }}
> >
<ol className="bg-base bg-op-70! backdrop-blur-md p-2 rounded-lg color-base text-base"> <ol className="bg-base bg-op-70! backdrop-blur-md p-2 rounded-lg color-base text-base">
{loggedIn {enableLogin && (loggedIn
? ( ? (
<li onClick={logout}> <li onClick={logout}>
<span className="i-ph:sign-out-duotone inline-block" /> <span className="i-ph:sign-out-duotone inline-block" />
@ -69,7 +69,7 @@ export function Menu() {
<span className="i-ph:sign-in-duotone inline-block" /> <span className="i-ph:sign-in-duotone inline-block" />
<span>Github </span> <span>Github </span>
</li> </li>
)} ))}
<ThemeToggle /> <ThemeToggle />
<li onClick={() => window.open(Homepage)}> <li onClick={() => window.open(Homepage)}>
<span className="i-ph:github-logo-duotone inline-block" /> <span className="i-ph:github-logo-duotone inline-block" />

View File

@ -5,12 +5,32 @@ const userAtom = atomWithStorage<{
const jwtAtom = atomWithStorage("jwt", "") const jwtAtom = atomWithStorage("jwt", "")
const enableLoginAtom = atomWithStorage<{
enable: boolean
url?: string
}>("login", {
enable: true,
})
enableLoginAtom.onMount = (set) => {
myFetch("/enable-login").then((r) => {
set(r)
}).catch((e) => {
if (e.statusCode === 506) {
set({ enable: false })
console.log("clear")
}
})
}
export function useLogin() { export function useLogin() {
const userInfo = useAtomValue(userAtom) const userInfo = useAtomValue(userAtom)
const jwt = useAtomValue(jwtAtom) const jwt = useAtomValue(jwtAtom)
const enableLogin = useAtomValue(enableLoginAtom)
const login = useCallback(() => { const login = useCallback(() => {
window.location.href = __LOGIN_URL__ window.location.href = enableLogin.url || "/api/login"
}, []) }, [enableLogin])
const logout = useCallback(() => { const logout = useCallback(() => {
window.localStorage.clear() window.localStorage.clear()
@ -20,6 +40,7 @@ export function useLogin() {
return { return {
loggedIn: !!jwt, loggedIn: !!jwt,
userInfo, userInfo,
enableLogin: !!enableLogin.enable,
logout, logout,
login, login,
} }

View File

@ -16,14 +16,26 @@ function initRefetchSources() {
const refetchSourcesAtom = atom(initRefetchSources()) const refetchSourcesAtom = atom(initRefetchSources())
export function useRefetch() { export function useRefetch() {
const [refetchSource, setRefetchSource] = useAtom(refetchSourcesAtom) const [refetchSource, setRefetchSource] = useAtom(refetchSourcesAtom)
const { enableLogin, loggedIn, login } = useLogin()
const toaster = useToast()
const refresh = useCallback((...sources: SourceID[]) => { const refresh = useCallback((...sources: SourceID[]) => {
const obj = Object.fromEntries(sources.map(id => [id, Date.now()])) if (loggedIn) {
setRefetchSource(prev => ({ const obj = Object.fromEntries(sources.map(id => [id, Date.now()]))
...prev, setRefetchSource(prev => ({
...obj, ...prev,
})) ...obj,
}, [setRefetchSource]) }))
} else if (enableLogin) {
toaster("登录后可以强制拉取最新数据", {
type: "warning",
action: {
label: "登录",
onClick: login,
},
})
}
}, [setRefetchSource, loggedIn, toaster, login, enableLogin])
const getRefreshId = useCallback((id: SourceID) => refetchSource[id], [refetchSource]) const getRefreshId = useCallback((id: SourceID) => refetchSource[id], [refetchSource])

View File

@ -7,7 +7,7 @@ import { safeParseString } from "~/utils"
async function uploadMetadata(metadata: PrimitiveMetadata) { async function uploadMetadata(metadata: PrimitiveMetadata) {
const jwt = safeParseString(localStorage.getItem("jwt")) const jwt = safeParseString(localStorage.getItem("jwt"))
if (!jwt) return if (!jwt) return
await myFetch("/api/me/sync", { await myFetch("/me/sync", {
method: "POST", method: "POST",
headers: { headers: {
Authorization: `Bearer ${jwt}`, Authorization: `Bearer ${jwt}`,
@ -22,7 +22,7 @@ async function uploadMetadata(metadata: PrimitiveMetadata) {
async function downloadMetadata(): Promise<PrimitiveMetadata | undefined> { async function downloadMetadata(): Promise<PrimitiveMetadata | undefined> {
const jwt = safeParseString(localStorage.getItem("jwt")) const jwt = safeParseString(localStorage.getItem("jwt"))
if (!jwt) return if (!jwt) return
const { data, updatedTime } = await myFetch("/api/me/sync", { const { data, updatedTime } = await myFetch("/me/sync", {
headers: { headers: {
Authorization: `Bearer ${jwt}`, Authorization: `Bearer ${jwt}`,
}, },

View File

@ -10,8 +10,6 @@ const router = createRouter({
context: { context: {
queryClient, queryClient,
}, },
defaultPreload: "intent",
defaultPreloadStaleTime: 0,
}) })
const rootElement = document.getElementById("app")! const rootElement = document.getElementById("app")!

View File

@ -15,14 +15,6 @@ export const Route = createRootRouteWithContext<{
}>()({ }>()({
component: RootComponent, component: RootComponent,
notFoundComponent: NotFoundComponent, notFoundComponent: NotFoundComponent,
beforeLoad: () => {
const query = new URLSearchParams(window.location.search)
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)
}
},
}) })
function NotFoundComponent() { function NotFoundComponent() {

View File

@ -40,4 +40,5 @@ export class Timer {
export const myFetch = $fetch.create({ export const myFetch = $fetch.create({
timeout: 15000, timeout: 15000,
retry: 0, retry: 0,
baseURL: "/api",
}) })

1
src/vite-env.d.ts vendored
View File

@ -2,4 +2,3 @@
/// <reference types="vite-plugin-pwa/react" /> /// <reference types="vite-plugin-pwa/react" />
/// <reference types="vite-plugin-pwa/info" /> /// <reference types="vite-plugin-pwa/info" />
/// <reference lib="webworker" /> /// <reference lib="webworker" />
declare const __LOGIN_URL__: string

View File

@ -1,4 +1,3 @@
import process from "node:process"
import { join } from "node:path" import { join } from "node:path"
import { defineConfig } from "vite" import { defineConfig } from "vite"
import react from "@vitejs/plugin-react-swc" import react from "@vitejs/plugin-react-swc"
@ -15,13 +14,6 @@ dotenv.config({
}) })
export default defineConfig({ export default defineConfig({
define: {
__LOGIN_URL__: process.env.G_CLIENT_ID ? `"https://github.com/login/oauth/authorize?client_id=${process.env.G_CLIENT_ID}"` : `"/api/login"`,
},
build: {
sourcemap: true,
minify: false,
},
resolve: { resolve: {
alias: { alias: {
"~": join(projectDir, "src"), "~": join(projectDir, "src"),