mirror of
https://github.com/ourongxing/newsnow.git
synced 2025-01-19 03:09:14 +08:00
feat: refactor login
This commit is contained in:
parent
75ab32d250
commit
773ab138ca
8
server/api/enable-login.ts
Normal file
8
server/api/enable-login.ts
Normal 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}`,
|
||||||
|
}
|
||||||
|
})
|
@ -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)) {
|
||||||
|
@ -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,
|
||||||
|
@ -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" />
|
||||||
|
@ -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,
|
||||||
}
|
}
|
||||||
|
@ -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])
|
||||||
|
|
||||||
|
@ -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}`,
|
||||||
},
|
},
|
||||||
|
@ -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")!
|
||||||
|
@ -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() {
|
||||||
|
@ -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
1
src/vite-env.d.ts
vendored
@ -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
|
|
||||||
|
@ -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"),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user