mirror of
https://github.com/ourongxing/newsnow.git
synced 2025-01-19 03:09:14 +08:00
feat: optimize auth logic
This commit is contained in:
parent
99b06e51cb
commit
4a3c9f586a
5
server/api/login.ts
Normal file
5
server/api/login.ts
Normal file
@ -0,0 +1,5 @@
|
||||
import process from "node:process"
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
return sendRedirect(event, `https://github.com/login/oauth/authorize?client_id=${process.env.G_CLIENT_ID}`)
|
||||
})
|
@ -1,3 +1,4 @@
|
||||
import process from "node:process"
|
||||
import { verifyPrimitiveMetadata } from "@shared/verify"
|
||||
import { UserTable } from "#/database/user"
|
||||
|
||||
@ -7,6 +8,7 @@ export default defineEventHandler(async (event) => {
|
||||
const db = useDatabase()
|
||||
if (!db) throw new Error("Not found database")
|
||||
const userTable = new UserTable(db)
|
||||
if (process.env.INIT_TABLE !== "false") await userTable.init()
|
||||
if (event.method === "GET") {
|
||||
const { data, updated } = await userTable.getData(id)
|
||||
return {
|
||||
|
@ -3,7 +3,6 @@ import { SignJWT } from "jose"
|
||||
import { UserTable } from "#/database/user"
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
if (["JWT_SECRET", "G_CLIENT_ID", "G_CLIENT_SECRET"].find(k => !process.env[k])) throw new Error("Missing environment variables")
|
||||
const db = useDatabase()
|
||||
const userTable = db ? new UserTable(db) : undefined
|
||||
if (!userTable) throw new Error("db is not defined")
|
||||
|
@ -5,11 +5,12 @@ export default defineEventHandler(async (event) => {
|
||||
const url = getRequestURL(event)
|
||||
if (["JWT_SECRET", "G_CLIENT_ID", "G_CLIENT_SECRET"].find(k => !process.env[k])) {
|
||||
event.context.disabledLogin = true
|
||||
if (url.pathname.startsWith("/api/me")) throw createError({ statusCode: 506, message: "Server not configured" })
|
||||
if (!url.pathname.startsWith("/api/s"))
|
||||
throw createError({ statusCode: 506, message: "Server not configured, disable login" })
|
||||
} else {
|
||||
if (/^\/api\/(?:me|s)\//.test(url.pathname)) {
|
||||
if (["/api/s", "/api/me"].find(p => url.pathname.startsWith(p))) {
|
||||
const token = getHeader(event, "Authorization")?.replace("Bearer ", "")?.trim()
|
||||
if (token && process.env.JWT_SECRET) {
|
||||
if (token) {
|
||||
try {
|
||||
const { payload } = await jwtVerify(token, new TextEncoder().encode(process.env.JWT_SECRET)) as { payload?: { id: string, type: string } }
|
||||
if (payload?.id) {
|
||||
@ -19,9 +20,12 @@ export default defineEventHandler(async (event) => {
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
if (url.pathname.startsWith("/api/me")) throw createError({ statusCode: 401, message: "JWT verification failed" })
|
||||
logger.warn("JWT verification failed")
|
||||
if (url.pathname.startsWith("/api/me"))
|
||||
throw createError({ statusCode: 401, message: "JWT verification failed" })
|
||||
else logger.warn("JWT verification failed")
|
||||
}
|
||||
} else if (url.pathname.startsWith("/api/me")) {
|
||||
throw createError({ statusCode: 401, message: "JWT verification failed" })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ function ThemeToggle() {
|
||||
}
|
||||
|
||||
export function Menu() {
|
||||
const { loggedIn, login, logout, enabledLogin, userInfo } = useLogin()
|
||||
const { loggedIn, login, logout, userInfo } = useLogin()
|
||||
const [shown, show] = useState(false)
|
||||
const ref = useRef<HTMLElement>(null)
|
||||
const isHover = useHoverDirty(ref)
|
||||
@ -30,7 +30,7 @@ export function Menu() {
|
||||
<span ref={ref} className="relative">
|
||||
<span className="flex items-center scale-90">
|
||||
{
|
||||
enabledLogin && loggedIn && userInfo.avatar
|
||||
loggedIn && userInfo.avatar
|
||||
? (
|
||||
<button
|
||||
type="button"
|
||||
@ -62,18 +62,19 @@ export function Menu() {
|
||||
}}
|
||||
>
|
||||
<ol className="bg-base bg-op-70! backdrop-blur-md p-2 rounded-lg color-base text-base">
|
||||
{enabledLogin && !loggedIn && (
|
||||
<li onClick={login}>
|
||||
<span className="i-ph:sign-in-duotone inline-block" />
|
||||
<span>Github 账号登录</span>
|
||||
</li>
|
||||
)}
|
||||
{enabledLogin && loggedIn && (
|
||||
<li onClick={logout}>
|
||||
<span className="i-ph:sign-out-duotone inline-block" />
|
||||
<span>退出登录</span>
|
||||
</li>
|
||||
)}
|
||||
{loggedIn
|
||||
? (
|
||||
<li onClick={logout}>
|
||||
<span className="i-ph:sign-out-duotone inline-block" />
|
||||
<span>退出登录</span>
|
||||
</li>
|
||||
)
|
||||
: (
|
||||
<li onClick={login}>
|
||||
<span className="i-ph:sign-in-duotone inline-block" />
|
||||
<span>Github 账号登录</span>
|
||||
</li>
|
||||
)}
|
||||
<ThemeToggle />
|
||||
<li onClick={() => window.open(Homepage)}>
|
||||
<span className="i-ph:github-logo-duotone inline-block" />
|
||||
|
@ -12,13 +12,11 @@ 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__}`
|
||||
window.location.href = __LOGIN_URL__
|
||||
}, [])
|
||||
|
||||
return {
|
||||
enabledLogin,
|
||||
loggedIn: !!jwt,
|
||||
userInfo,
|
||||
logout: () => setJwt(""),
|
||||
|
@ -8,27 +8,21 @@ import { preprocessMetadata, primitiveMetadataAtom } from "~/atoms"
|
||||
import { safeParseString } from "~/utils"
|
||||
|
||||
export async function uploadMetadata(metadata: PrimitiveMetadata) {
|
||||
if (!__ENABLE_LOGIN__) return
|
||||
const jwt = safeParseString(localStorage.getItem("jwt"))
|
||||
if (!jwt) return
|
||||
try {
|
||||
await ofetch("/api/me/sync", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
Authorization: `Bearer ${jwt}`,
|
||||
},
|
||||
body: {
|
||||
data: metadata.data,
|
||||
updatedTime: metadata.updatedTime,
|
||||
},
|
||||
})
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
await ofetch("/api/me/sync", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
Authorization: `Bearer ${jwt}`,
|
||||
},
|
||||
body: {
|
||||
data: metadata.data,
|
||||
updatedTime: metadata.updatedTime,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
export async function downloadMetadata(): Promise<PrimitiveMetadata | undefined> {
|
||||
if (!__ENABLE_LOGIN__) return
|
||||
const jwt = safeParseString(localStorage.getItem("jwt"))
|
||||
if (!jwt) return
|
||||
const { data, updatedTime } = await ofetch("/api/me/sync", {
|
||||
@ -52,8 +46,25 @@ export function useSync() {
|
||||
const toaster = useToast()
|
||||
|
||||
useDebounce(async () => {
|
||||
const fn = async () => {
|
||||
try {
|
||||
await uploadMetadata(primitiveMetadata)
|
||||
} catch (e: any) {
|
||||
if (e.statusCode !== 506) {
|
||||
toaster("身份校验失败,无法同步,请重新登录", {
|
||||
type: "error",
|
||||
action: {
|
||||
label: "登录",
|
||||
onClick: login,
|
||||
},
|
||||
})
|
||||
logout()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (primitiveMetadata.action === "manual") {
|
||||
uploadMetadata(primitiveMetadata)
|
||||
fn()
|
||||
}
|
||||
}, 10000, [primitiveMetadata])
|
||||
useMount(() => {
|
||||
@ -64,8 +75,8 @@ export function useSync() {
|
||||
setPrimitiveMetadata(preprocessMetadata(metadata))
|
||||
}
|
||||
} catch (e: any) {
|
||||
if (e.statusCode === 401) {
|
||||
toaster("身份校验失败,请重新登录", {
|
||||
if (e.statusCode !== 506) {
|
||||
toaster("身份校验失败,无法同步,请重新登录", {
|
||||
type: "error",
|
||||
action: {
|
||||
label: "登录",
|
||||
|
@ -17,11 +17,13 @@ import { Route as CColumnImport } from './routes/c.$column'
|
||||
// Create/Update Routes
|
||||
|
||||
const IndexRoute = IndexImport.update({
|
||||
id: '/',
|
||||
path: '/',
|
||||
getParentRoute: () => rootRoute,
|
||||
} as any)
|
||||
|
||||
const CColumnRoute = CColumnImport.update({
|
||||
id: '/c/$column',
|
||||
path: '/c/$column',
|
||||
getParentRoute: () => rootRoute,
|
||||
} as any)
|
||||
|
3
src/vite-env.d.ts
vendored
3
src/vite-env.d.ts
vendored
@ -2,5 +2,4 @@
|
||||
/// <reference types="vite-plugin-pwa/react" />
|
||||
/// <reference types="vite-plugin-pwa/info" />
|
||||
/// <reference lib="webworker" />
|
||||
declare const __G_CLIENT_ID__: string
|
||||
declare const __ENABLE_LOGIN__: boolean
|
||||
declare const __LOGIN_URL__: string
|
||||
|
@ -97,8 +97,7 @@ if (process.env.VERCEL) {
|
||||
|
||||
export default defineConfig({
|
||||
define: {
|
||||
__G_CLIENT_ID__: `"${process.env.G_CLIENT_ID}"`,
|
||||
__ENABLE_LOGIN__: ["JWT_SECRET", "G_CLIENT_ID", "G_CLIENT_SECRET"].every(k => process.env[k]),
|
||||
__LOGIN_URL__: process.env.G_CLIENT_ID ? `"https://github.com/login/oauth/authorize?client_id=${process.env.G_CLIENT_ID}"` : `"/api/login"`,
|
||||
},
|
||||
plugins: [
|
||||
tsconfigPath(),
|
||||
|
Loading…
x
Reference in New Issue
Block a user