feat: add support for request ID tracking across services

- Implemented request ID handling in various services and handlers to improve traceability of requests.
- Added new AuthOptions endpoint to expose WxWork and OIDC configuration options.
- Updated message and event logging to include request ID for better debugging.
- Enhanced login form to dynamically show available authentication options based on server configuration.
- Introduced utility functions for normalizing and ensuring valid request IDs.
- Updated tests to verify request ID functionality in message sending and event logging.
This commit is contained in:
mlogclub
2026-05-27 22:18:43 +08:00
parent 498932de61
commit c246b85a9e
30 changed files with 504 additions and 59 deletions
+56 -28
View File
@@ -6,7 +6,7 @@ import { startTransition, useEffect, useState } from "react"
import { toast } from "sonner"
import { useAuth } from "@/components/auth-provider"
import { loginWithPassword } from "@/lib/api/auth"
import { fetchAuthOptions, loginWithPassword, type AuthOptions } from "@/lib/api/auth"
import { useI18n } from "@/i18n/provider"
import { cn } from "@/lib/utils"
import { Button } from "@/components/ui/button"
@@ -36,6 +36,10 @@ export function LoginForm({
const { session } = useAuth()
const [isPending, setIsPending] = useState(false)
const [isWxWorkEnv, setIsWxWorkEnv] = useState(false)
const [authOptions, setAuthOptions] = useState<AuthOptions>({
wxworkEnabled: false,
oidcEnabled: false,
})
const nextPath = searchParams.get("next")
const wxworkError = searchParams.get("wxworkError")
const oidcError = searchParams.get("oidcError")
@@ -64,6 +68,26 @@ export function LoginForm({
setIsWxWorkEnv(detectWxWorkEnvironment())
}, [])
useEffect(() => {
let cancelled = false
void fetchAuthOptions()
.then((options) => {
if (!cancelled) {
setAuthOptions(options)
}
})
.catch(() => {
if (!cancelled) {
setAuthOptions({ wxworkEnabled: false, oidcEnabled: false })
}
})
return () => {
cancelled = true
}
}, [])
async function handleSubmit(event: React.FormEvent<HTMLFormElement>) {
event.preventDefault()
const formData = new FormData(event.currentTarget)
@@ -126,33 +150,37 @@ export function LoginForm({
{isPending ? t("auth.signingIn") : t("auth.signIn")}
</Button>
</Field>
<Field>
<Button
type="button"
variant="outline"
className="gap-2"
onClick={() => {
const path = isWxWorkEnv ? "/api/auth/wxwork_login" : "/api/auth/wxwork_qr_login"
window.location.href = `${path}?next=${encodeURIComponent(redirectPath)}`
}}
>
<Image src="/images/wxwork.svg" alt="" width={16} height={16} className="size-4 shrink-0" />
{t("auth.wxworkSignIn")}
</Button>
</Field>
<Field>
<Button
type="button"
variant="outline"
className="gap-2"
onClick={() => {
window.location.href = `/api/auth/oidc_login?next=${encodeURIComponent(redirectPath)}`
}}
>
<KeyRoundIcon className="size-4 shrink-0" />
{t("auth.oidcSignIn")}
</Button>
</Field>
{authOptions.wxworkEnabled ? (
<Field>
<Button
type="button"
variant="outline"
className="gap-2"
onClick={() => {
const path = isWxWorkEnv ? "/api/auth/wxwork_login" : "/api/auth/wxwork_qr_login"
window.location.href = `${path}?next=${encodeURIComponent(redirectPath)}`
}}
>
<Image src="/images/wxwork.svg" alt="" width={16} height={16} className="size-4 shrink-0" />
{t("auth.wxworkSignIn")}
</Button>
</Field>
) : null}
{authOptions.oidcEnabled ? (
<Field>
<Button
type="button"
variant="outline"
className="gap-2"
onClick={() => {
window.location.href = `/api/auth/oidc_login?next=${encodeURIComponent(redirectPath)}`
}}
>
<KeyRoundIcon className="size-4 shrink-0" />
{t("auth.oidcSignIn")}
</Button>
</Field>
) : null}
</FieldGroup>
</form>
)
+11
View File
@@ -6,6 +6,17 @@ export type LoginRequest = {
password: string
}
export type AuthOptions = {
wxworkEnabled: boolean
oidcEnabled: boolean
}
export async function fetchAuthOptions() {
return request<AuthOptions>("/api/auth/options", {
skipAuth: true,
})
}
export async function loginWithPassword(payload: LoginRequest) {
const data = await request<AuthSession>("/api/auth/login", {
method: "POST",