first commit
This commit is contained in:
371
dist/react/index.js
vendored
Normal file
371
dist/react/index.js
vendored
Normal file
@@ -0,0 +1,371 @@
|
||||
// react/AuthGuard.tsx
|
||||
import { useEffect, useState } from "react";
|
||||
import { Center, Spinner } from "@chakra-ui/react";
|
||||
import { Navigate } from "react-router-dom";
|
||||
import { Fragment, jsx } from "react/jsx-runtime";
|
||||
function AuthGuard({
|
||||
children,
|
||||
fetchCurrentUser,
|
||||
redirectTo = "/login",
|
||||
loadingFallback,
|
||||
authenticatedWrapper
|
||||
}) {
|
||||
const [state, setState] = useState({
|
||||
loading: true,
|
||||
authenticated: false
|
||||
});
|
||||
useEffect(() => {
|
||||
let cancelled = false;
|
||||
fetchCurrentUser().then(() => {
|
||||
if (!cancelled) {
|
||||
setState({ loading: false, authenticated: true });
|
||||
}
|
||||
}).catch(() => {
|
||||
if (!cancelled) {
|
||||
setState({ loading: false, authenticated: false });
|
||||
}
|
||||
});
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, [fetchCurrentUser]);
|
||||
if (state.loading) {
|
||||
return /* @__PURE__ */ jsx(Fragment, { children: loadingFallback ?? /* @__PURE__ */ jsx(Center, { h: "var(--app-height)", children: /* @__PURE__ */ jsx(Spinner, { size: "xl" }) }) });
|
||||
}
|
||||
if (!state.authenticated) {
|
||||
return /* @__PURE__ */ jsx(Navigate, { to: redirectTo, replace: true });
|
||||
}
|
||||
return /* @__PURE__ */ jsx(Fragment, { children: authenticatedWrapper ? authenticatedWrapper(children) : children });
|
||||
}
|
||||
|
||||
// react/client.ts
|
||||
async function readJsonError(response, fallback) {
|
||||
const payload = await response.json().catch(() => null);
|
||||
return new Error(payload?.error ?? fallback);
|
||||
}
|
||||
function createAuthClient(options) {
|
||||
const fetchImpl = options.fetchImpl ?? fetch;
|
||||
const authUrl = options.authUrl ?? options.apiUrl;
|
||||
const credentials = options.credentials ?? "include";
|
||||
function resolveDefaultOAuthCallbackUrl() {
|
||||
const configured = options.defaultOAuthCallbackUrl;
|
||||
if (typeof configured === "function") {
|
||||
return configured();
|
||||
}
|
||||
if (typeof configured === "string" && configured.trim().length > 0) {
|
||||
return configured;
|
||||
}
|
||||
return `${window.location.origin}/chat`;
|
||||
}
|
||||
async function request(path, init) {
|
||||
return fetchImpl(options.apiUrl(path), {
|
||||
...init,
|
||||
credentials,
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
...init?.headers ?? {}
|
||||
}
|
||||
});
|
||||
}
|
||||
return {
|
||||
async getProviders() {
|
||||
const response = await request("/api/auth/providers");
|
||||
if (!response.ok) {
|
||||
throw await readJsonError(response, "providers_unavailable");
|
||||
}
|
||||
return await response.json();
|
||||
},
|
||||
async getCurrentUser() {
|
||||
const response = await request("/api/me");
|
||||
if (!response.ok) {
|
||||
throw await readJsonError(response, "Unauthorized");
|
||||
}
|
||||
const payload = await response.json();
|
||||
return payload.user;
|
||||
},
|
||||
async register(input) {
|
||||
const response = await request("/api/auth/register", {
|
||||
method: "POST",
|
||||
body: JSON.stringify(input)
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw await readJsonError(response, "Registration failed");
|
||||
}
|
||||
},
|
||||
async login(input) {
|
||||
const response = await request("/api/auth/login", {
|
||||
method: "POST",
|
||||
body: JSON.stringify(input)
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw await readJsonError(response, "Sign in failed");
|
||||
}
|
||||
},
|
||||
async requestPasswordReset(email) {
|
||||
const response = await request("/api/auth/password-reset/request", {
|
||||
method: "POST",
|
||||
body: JSON.stringify({ email })
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw await readJsonError(response, "Password reset request failed");
|
||||
}
|
||||
},
|
||||
async validatePasswordResetToken(token) {
|
||||
const response = await request(`/api/auth/password-reset/validate?token=${encodeURIComponent(token)}`, {
|
||||
headers: {}
|
||||
});
|
||||
const payload = await response.json().catch(() => null);
|
||||
if (!response.ok || !payload?.email || payload.mode !== "reset" && payload.mode !== "create") {
|
||||
throw new Error(payload?.error ?? "Invalid reset link");
|
||||
}
|
||||
return {
|
||||
email: payload.email,
|
||||
mode: payload.mode
|
||||
};
|
||||
},
|
||||
async confirmPasswordReset(input) {
|
||||
const response = await request("/api/auth/password-reset/confirm", {
|
||||
method: "POST",
|
||||
body: JSON.stringify(input)
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw await readJsonError(response, "Invalid reset link");
|
||||
}
|
||||
},
|
||||
async logout() {
|
||||
const response = await request("/api/auth/logout", {
|
||||
method: "POST"
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw await readJsonError(response, "Logout failed");
|
||||
}
|
||||
},
|
||||
async startOAuthSignIn(provider, callbackUrl = resolveDefaultOAuthCallbackUrl()) {
|
||||
const response = await fetchImpl(authUrl("/auth/csrf"), {
|
||||
credentials
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw await readJsonError(response, "Sign in failed");
|
||||
}
|
||||
const payload = await response.json();
|
||||
if (!payload.csrfToken) {
|
||||
throw new Error("Sign in failed");
|
||||
}
|
||||
const form = document.createElement("form");
|
||||
form.method = "POST";
|
||||
form.action = authUrl(`/auth/signin/${provider}`);
|
||||
form.style.display = "none";
|
||||
const csrfInput = document.createElement("input");
|
||||
csrfInput.type = "hidden";
|
||||
csrfInput.name = "csrfToken";
|
||||
csrfInput.value = payload.csrfToken;
|
||||
form.appendChild(csrfInput);
|
||||
const callbackInput = document.createElement("input");
|
||||
callbackInput.type = "hidden";
|
||||
callbackInput.name = "callbackUrl";
|
||||
callbackInput.value = callbackUrl;
|
||||
form.appendChild(callbackInput);
|
||||
document.body.appendChild(form);
|
||||
form.submit();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// react/LoginForm.tsx
|
||||
import { Alert, AlertDescription, AlertIcon, Button, Center as Center2, FormControl, FormLabel, HStack, Icon, Input, Stack } from "@chakra-ui/react";
|
||||
import { FcGoogle } from "react-icons/fc";
|
||||
import { jsx as jsx2, jsxs } from "react/jsx-runtime";
|
||||
function LoginForm({
|
||||
mode,
|
||||
texts,
|
||||
onSubmit,
|
||||
onModeToggle,
|
||||
loading = false,
|
||||
oauthLoadingProvider = null,
|
||||
providers,
|
||||
onOAuthSignIn,
|
||||
errorMessage,
|
||||
successMessage,
|
||||
footer,
|
||||
forgotPasswordLink,
|
||||
emailPlaceholder = "you@example.com",
|
||||
namePlaceholder = "Jane Doe"
|
||||
}) {
|
||||
const registerMode = mode === "register";
|
||||
function handleSubmit(event) {
|
||||
event.preventDefault();
|
||||
const form = new FormData(event.currentTarget);
|
||||
void onSubmit({
|
||||
name: String(form.get("name") ?? ""),
|
||||
email: String(form.get("email") ?? ""),
|
||||
password: String(form.get("password") ?? ""),
|
||||
passwordConfirm: String(form.get("passwordConfirm") ?? "")
|
||||
});
|
||||
}
|
||||
return /* @__PURE__ */ jsxs(Stack, { spacing: 5, children: [
|
||||
errorMessage ? /* @__PURE__ */ jsxs(Alert, { status: "error", borderRadius: "md", children: [
|
||||
/* @__PURE__ */ jsx2(AlertIcon, {}),
|
||||
/* @__PURE__ */ jsx2(AlertDescription, { children: errorMessage })
|
||||
] }) : null,
|
||||
successMessage ? /* @__PURE__ */ jsxs(Alert, { status: "success", borderRadius: "md", children: [
|
||||
/* @__PURE__ */ jsx2(AlertIcon, {}),
|
||||
/* @__PURE__ */ jsx2(AlertDescription, { children: successMessage })
|
||||
] }) : null,
|
||||
/* @__PURE__ */ jsx2("form", { onSubmit: handleSubmit, children: /* @__PURE__ */ jsxs(Stack, { spacing: 4, children: [
|
||||
registerMode ? /* @__PURE__ */ jsxs(FormControl, { isRequired: true, children: [
|
||||
/* @__PURE__ */ jsx2(FormLabel, { children: texts.nameLabel }),
|
||||
/* @__PURE__ */ jsx2(Input, { name: "name", placeholder: namePlaceholder })
|
||||
] }) : null,
|
||||
/* @__PURE__ */ jsxs(FormControl, { isRequired: true, children: [
|
||||
/* @__PURE__ */ jsx2(FormLabel, { children: texts.emailLabel }),
|
||||
/* @__PURE__ */ jsx2(Input, { name: "email", type: "email", placeholder: emailPlaceholder })
|
||||
] }),
|
||||
/* @__PURE__ */ jsxs(FormControl, { isRequired: true, children: [
|
||||
/* @__PURE__ */ jsx2(FormLabel, { children: texts.passwordLabel }),
|
||||
/* @__PURE__ */ jsx2(Input, { name: "password", type: "password", minLength: 8 })
|
||||
] }),
|
||||
!registerMode && forgotPasswordLink ? /* @__PURE__ */ jsx2(Stack, { align: "flex-end", children: forgotPasswordLink }) : null,
|
||||
registerMode ? /* @__PURE__ */ jsxs(FormControl, { isRequired: true, children: [
|
||||
/* @__PURE__ */ jsx2(FormLabel, { children: texts.passwordConfirmLabel }),
|
||||
/* @__PURE__ */ jsx2(Input, { name: "passwordConfirm", type: "password", minLength: 8 })
|
||||
] }) : null,
|
||||
/* @__PURE__ */ jsx2(Button, { type: "submit", isLoading: loading, children: registerMode ? texts.submitRegisterLabel : texts.submitSignInLabel })
|
||||
] }) }),
|
||||
registerMode || !onOAuthSignIn || !providers?.google && !providers?.slack ? null : /* @__PURE__ */ jsxs(HStack, { children: [
|
||||
providers.google ? /* @__PURE__ */ jsx2(
|
||||
Button,
|
||||
{
|
||||
flex: 1,
|
||||
bg: "gray.200",
|
||||
color: "gray.900",
|
||||
borderRadius: "full",
|
||||
justifyContent: "flex-start",
|
||||
h: "56px",
|
||||
px: 3,
|
||||
fontSize: { base: "md", md: "lg" },
|
||||
fontWeight: "semibold",
|
||||
iconSpacing: 4,
|
||||
leftIcon: /* @__PURE__ */ jsx2(Center2, { boxSize: "40px", bg: "white", borderRadius: "full", boxShadow: "sm", children: /* @__PURE__ */ jsx2(Icon, { as: FcGoogle, boxSize: 6 }) }),
|
||||
_hover: { bg: "gray.300" },
|
||||
_active: { bg: "gray.300" },
|
||||
isLoading: oauthLoadingProvider === "google",
|
||||
onClick: () => void onOAuthSignIn("google"),
|
||||
children: texts.googleLabel
|
||||
}
|
||||
) : null,
|
||||
providers.slack ? /* @__PURE__ */ jsx2(
|
||||
Button,
|
||||
{
|
||||
flex: 1,
|
||||
variant: "outline",
|
||||
isLoading: oauthLoadingProvider === "slack",
|
||||
onClick: () => void onOAuthSignIn("slack"),
|
||||
children: texts.slackLabel
|
||||
}
|
||||
) : null
|
||||
] }),
|
||||
/* @__PURE__ */ jsx2(Button, { variant: "ghost", onClick: onModeToggle, children: registerMode ? texts.toggleToSignInLabel : texts.toggleToRegisterLabel }),
|
||||
footer ?? null
|
||||
] });
|
||||
}
|
||||
|
||||
// react/PasswordResetForms.tsx
|
||||
import {
|
||||
Alert as Alert2,
|
||||
AlertDescription as AlertDescription2,
|
||||
AlertIcon as AlertIcon2,
|
||||
Button as Button2,
|
||||
FormControl as FormControl2,
|
||||
FormLabel as FormLabel2,
|
||||
Input as Input2,
|
||||
Spinner as Spinner2,
|
||||
Stack as Stack2,
|
||||
Text
|
||||
} from "@chakra-ui/react";
|
||||
import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
||||
function PasswordResetRequestForm({
|
||||
texts,
|
||||
helperText,
|
||||
loading = false,
|
||||
requestSent = false,
|
||||
onSubmit,
|
||||
emailPlaceholder = "you@example.com"
|
||||
}) {
|
||||
function handleSubmit(event) {
|
||||
event.preventDefault();
|
||||
const form = new FormData(event.currentTarget);
|
||||
void onSubmit({
|
||||
email: String(form.get("email") ?? "")
|
||||
});
|
||||
}
|
||||
return /* @__PURE__ */ jsxs2(Stack2, { spacing: 5, children: [
|
||||
requestSent ? /* @__PURE__ */ jsxs2(Alert2, { status: "success", borderRadius: "md", children: [
|
||||
/* @__PURE__ */ jsx3(AlertIcon2, {}),
|
||||
/* @__PURE__ */ jsx3(AlertDescription2, { children: texts.requestSentMessage })
|
||||
] }) : null,
|
||||
/* @__PURE__ */ jsx3("form", { onSubmit: handleSubmit, children: /* @__PURE__ */ jsxs2(Stack2, { spacing: 4, children: [
|
||||
/* @__PURE__ */ jsxs2(FormControl2, { isRequired: true, children: [
|
||||
/* @__PURE__ */ jsx3(FormLabel2, { children: texts.emailLabel }),
|
||||
/* @__PURE__ */ jsx3(Input2, { name: "email", type: "email", placeholder: emailPlaceholder })
|
||||
] }),
|
||||
/* @__PURE__ */ jsx3(Text, { fontSize: "sm", color: "gray.600", children: helperText }),
|
||||
/* @__PURE__ */ jsx3(Button2, { type: "submit", isLoading: loading, children: texts.submitLabel })
|
||||
] }) })
|
||||
] });
|
||||
}
|
||||
function PasswordResetConfirmForm({
|
||||
texts,
|
||||
tokenState,
|
||||
loading = false,
|
||||
completedMode = null,
|
||||
onSubmit
|
||||
}) {
|
||||
function handleSubmit(event) {
|
||||
event.preventDefault();
|
||||
const form = new FormData(event.currentTarget);
|
||||
void onSubmit({
|
||||
password: String(form.get("password") ?? ""),
|
||||
passwordConfirm: String(form.get("passwordConfirm") ?? "")
|
||||
});
|
||||
}
|
||||
if (tokenState.status === "loading") {
|
||||
return /* @__PURE__ */ jsxs2(Stack2, { align: "center", py: 6, spacing: 3, children: [
|
||||
/* @__PURE__ */ jsx3(Spinner2, {}),
|
||||
/* @__PURE__ */ jsx3(Text, { color: "gray.600", children: texts.loadingLabel })
|
||||
] });
|
||||
}
|
||||
if (tokenState.status === "invalid") {
|
||||
return /* @__PURE__ */ jsxs2(Alert2, { status: "error", borderRadius: "md", children: [
|
||||
/* @__PURE__ */ jsx3(AlertIcon2, {}),
|
||||
/* @__PURE__ */ jsx3(AlertDescription2, { children: tokenState.error || texts.invalidLinkLabel })
|
||||
] });
|
||||
}
|
||||
if (completedMode !== null) {
|
||||
return /* @__PURE__ */ jsxs2(Alert2, { status: "success", borderRadius: "md", children: [
|
||||
/* @__PURE__ */ jsx3(AlertIcon2, {}),
|
||||
/* @__PURE__ */ jsx3(AlertDescription2, { children: completedMode === "create" ? texts.createSuccessLabel : texts.resetSuccessLabel })
|
||||
] });
|
||||
}
|
||||
return /* @__PURE__ */ jsxs2(Stack2, { spacing: 4, children: [
|
||||
/* @__PURE__ */ jsx3(Text, { fontSize: "sm", color: "gray.600", children: tokenState.email }),
|
||||
/* @__PURE__ */ jsx3("form", { onSubmit: handleSubmit, children: /* @__PURE__ */ jsxs2(Stack2, { spacing: 4, children: [
|
||||
/* @__PURE__ */ jsxs2(FormControl2, { isRequired: true, children: [
|
||||
/* @__PURE__ */ jsx3(FormLabel2, { children: texts.passwordLabel }),
|
||||
/* @__PURE__ */ jsx3(Input2, { name: "password", type: "password", minLength: 8 })
|
||||
] }),
|
||||
/* @__PURE__ */ jsxs2(FormControl2, { isRequired: true, children: [
|
||||
/* @__PURE__ */ jsx3(FormLabel2, { children: texts.passwordConfirmLabel }),
|
||||
/* @__PURE__ */ jsx3(Input2, { name: "passwordConfirm", type: "password", minLength: 8 })
|
||||
] }),
|
||||
/* @__PURE__ */ jsx3(Button2, { type: "submit", isLoading: loading, children: tokenState.mode === "create" ? texts.createSubmitLabel : texts.resetSubmitLabel })
|
||||
] }) })
|
||||
] });
|
||||
}
|
||||
export {
|
||||
AuthGuard,
|
||||
LoginForm,
|
||||
PasswordResetConfirmForm,
|
||||
PasswordResetRequestForm,
|
||||
createAuthClient
|
||||
};
|
||||
//# sourceMappingURL=index.js.map
|
||||
Reference in New Issue
Block a user