@hachej/boring-core 0.1.48 → 0.1.50
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{PostgresMeteringStore-DhOVVtau.d.ts → PostgresMeteringStore-qjKGmVFr.d.ts} +1 -1
- package/dist/app/front/chatFirst/chatFirstPublicShell.css +269 -0
- package/dist/app/front/index.js +80 -40
- package/dist/app/front/styles.css +47 -0
- package/dist/app/server/index.d.ts +4 -4
- package/dist/app/server/index.js +4 -3
- package/dist/{authHook-DtzhSmqS.d.ts → authHook-BbgCBHjP.d.ts} +3 -3
- package/dist/{chunk-VYXEXOCO.js → chunk-2ZTKRLVG.js} +118 -34
- package/dist/{chunk-FZC3VL5D.js → chunk-BVZ2YT3M.js} +1 -1
- package/dist/chunk-I4PGL4ZD.js +17 -0
- package/dist/{chunk-LIBHVT7V.js → chunk-QZGYKLXB.js} +1 -0
- package/dist/{chunk-6GAQRQKO.js → chunk-ZMS6O4CY.js} +26 -11
- package/dist/{connection-C5SiqoNc.d.ts → connection-B1iC6B-w.d.ts} +3 -2
- package/dist/front/index.d.ts +3 -2
- package/dist/front/index.js +3 -2
- package/dist/server/db/index.d.ts +4 -4
- package/dist/server/db/index.js +2 -2
- package/dist/server/index.d.ts +53 -48
- package/dist/server/index.js +8 -6
- package/dist/shared/index.d.ts +7 -13
- package/dist/shared/index.js +9 -1
- package/dist/telemetry-DR18MeI0.d.ts +13 -0
- package/dist/{types-CWtJ4kgd.d.ts → types-Bb7I-83I.d.ts} +3 -1
- package/package.json +6 -5
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
import {
|
|
2
2
|
TopBarSlotProvider
|
|
3
3
|
} from "./chunk-HYNKZSTF.js";
|
|
4
|
+
import {
|
|
5
|
+
canUseProtectedApi,
|
|
6
|
+
isRuntimeEmailVerificationEnabled
|
|
7
|
+
} from "./chunk-I4PGL4ZD.js";
|
|
4
8
|
import {
|
|
5
9
|
ConfigFetchError,
|
|
6
10
|
ERROR_CODES,
|
|
7
11
|
HttpError
|
|
8
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-QZGYKLXB.js";
|
|
9
13
|
|
|
10
14
|
// src/front/AppErrorBoundary.tsx
|
|
11
15
|
import { Component } from "react";
|
|
@@ -660,11 +664,16 @@ function WorkspaceAuthProvider({
|
|
|
660
664
|
const queryClient = useQueryClient();
|
|
661
665
|
const routeWorkspaceId = id?.trim() ? id : workspaceIdFromPath(location.pathname, workspaceRoute, workspaceIdParam);
|
|
662
666
|
const session = useSession();
|
|
663
|
-
const
|
|
667
|
+
const config = useOptionalConfig();
|
|
668
|
+
const user = session.data?.user ?? null;
|
|
669
|
+
const canQueryProtectedApi = canUseProtectedApi(
|
|
670
|
+
user,
|
|
671
|
+
isRuntimeEmailVerificationEnabled(config)
|
|
672
|
+
);
|
|
664
673
|
const workspacesQuery = useQuery2({
|
|
665
674
|
queryKey: WORKSPACES_QUERY_KEY,
|
|
666
675
|
queryFn: fetchWorkspaces,
|
|
667
|
-
enabled:
|
|
676
|
+
enabled: canQueryProtectedApi
|
|
668
677
|
});
|
|
669
678
|
const defaultWorkspace = routeWorkspaceId === null ? workspacesQuery.data?.find((workspace2) => workspace2.isDefault) ?? workspacesQuery.data?.[0] ?? null : null;
|
|
670
679
|
const resolvedId = routeWorkspaceId ?? defaultWorkspace?.id ?? null;
|
|
@@ -677,13 +686,13 @@ function WorkspaceAuthProvider({
|
|
|
677
686
|
}
|
|
678
687
|
return fetchWorkspace(resolvedId);
|
|
679
688
|
},
|
|
680
|
-
enabled:
|
|
689
|
+
enabled: canQueryProtectedApi && resolvedId !== null
|
|
681
690
|
});
|
|
682
691
|
const detail = detailQuery.data ?? cachedDetail ?? null;
|
|
683
692
|
const workspace = detailQuery.isError ? null : detail?.workspace ?? null;
|
|
684
693
|
const role = detailQuery.isError ? null : detail?.role ?? null;
|
|
685
694
|
const routeStatus = (() => {
|
|
686
|
-
if (!
|
|
695
|
+
if (!canQueryProtectedApi) return { status: "idle", workspaceId: routeWorkspaceId };
|
|
687
696
|
if (routeWorkspaceId === null) return { status: "idle", workspaceId: null };
|
|
688
697
|
if (detailQuery.isError) return routeStatusFromError(routeWorkspaceId, detailQuery.error);
|
|
689
698
|
if (detailQuery.isPending && !detail) return { status: "loading", workspaceId: routeWorkspaceId };
|
|
@@ -709,11 +718,16 @@ var UserContext = createContext5(null);
|
|
|
709
718
|
var STALE_MS = 6e4;
|
|
710
719
|
function UserIdentityProvider({ children }) {
|
|
711
720
|
const { data: session } = useSession();
|
|
721
|
+
const config = useOptionalConfig();
|
|
722
|
+
const canFetchIdentity = canUseProtectedApi(
|
|
723
|
+
session?.user,
|
|
724
|
+
isRuntimeEmailVerificationEnabled(config)
|
|
725
|
+
);
|
|
712
726
|
const [identity, setIdentity] = useState6(null);
|
|
713
727
|
const fetchedForRef = useRef3(null);
|
|
714
728
|
const lastFetchRef = useRef3(0);
|
|
715
729
|
useEffect7(() => {
|
|
716
|
-
if (!session?.user) {
|
|
730
|
+
if (!session?.user || !canFetchIdentity) {
|
|
717
731
|
setIdentity(null);
|
|
718
732
|
fetchedForRef.current = null;
|
|
719
733
|
return;
|
|
@@ -736,7 +750,7 @@ function UserIdentityProvider({ children }) {
|
|
|
736
750
|
return () => {
|
|
737
751
|
cancelled = true;
|
|
738
752
|
};
|
|
739
|
-
}, [session?.user?.id]);
|
|
753
|
+
}, [canFetchIdentity, session?.user?.id]);
|
|
740
754
|
return /* @__PURE__ */ jsx6(UserContext.Provider, { value: identity, children });
|
|
741
755
|
}
|
|
742
756
|
function useUser() {
|
|
@@ -892,6 +906,7 @@ function SignInPage() {
|
|
|
892
906
|
// src/front/auth/SignUpPage.tsx
|
|
893
907
|
import { useState as useState9 } from "react";
|
|
894
908
|
import { useForm as useForm2 } from "react-hook-form";
|
|
909
|
+
import { useNavigate } from "react-router-dom";
|
|
895
910
|
import { zodResolver as zodResolver2 } from "@hookform/resolvers/zod";
|
|
896
911
|
import { z as z2 } from "zod";
|
|
897
912
|
import { Button as Button4, Card as Card2, CardContent as CardContent2, CardDescription as CardDescription2, CardFooter as CardFooter2, CardHeader as CardHeader2, CardTitle as CardTitle2, Input as Input2, Label as Label2 } from "@hachej/boring-ui-kit";
|
|
@@ -909,6 +924,7 @@ function readGoogleAuthError2() {
|
|
|
909
924
|
}
|
|
910
925
|
function SignUpPage() {
|
|
911
926
|
const signUp = useSignUp();
|
|
927
|
+
const navigate = useNavigate();
|
|
912
928
|
const config = useOptionalConfig();
|
|
913
929
|
const [serverError, setServerError] = useState9(null);
|
|
914
930
|
const [oauthError, setOauthError] = useState9(() => readGoogleAuthError2());
|
|
@@ -935,6 +951,8 @@ function SignUpPage() {
|
|
|
935
951
|
);
|
|
936
952
|
if (result.error) {
|
|
937
953
|
setServerError(result.error.message ?? "Sign up failed");
|
|
954
|
+
} else if (isRuntimeEmailVerificationEnabled(config)) {
|
|
955
|
+
navigate(routes.verifyEmail, { replace: true });
|
|
938
956
|
} else {
|
|
939
957
|
setSuccess(true);
|
|
940
958
|
}
|
|
@@ -1214,16 +1232,23 @@ function getCookie(name) {
|
|
|
1214
1232
|
function deleteCookie(name) {
|
|
1215
1233
|
document.cookie = `${name}=; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT`;
|
|
1216
1234
|
}
|
|
1235
|
+
function navigateToSignIn() {
|
|
1236
|
+
if (typeof window === "undefined") return;
|
|
1237
|
+
window.history.replaceState({}, "", routes.signin);
|
|
1238
|
+
window.dispatchEvent(new PopStateEvent("popstate"));
|
|
1239
|
+
}
|
|
1217
1240
|
function VerifyEmailPage() {
|
|
1218
1241
|
const session = useSession();
|
|
1219
1242
|
const verifyEmail = useVerifyEmail();
|
|
1220
1243
|
const sendVerificationEmail = useSendVerificationEmail();
|
|
1244
|
+
const signOut = useSignOut();
|
|
1221
1245
|
const token = typeof window !== "undefined" ? new URLSearchParams(window.location.search).get("token") : null;
|
|
1222
1246
|
const [status, setStatus] = useState12(token ? "verifying" : "no-token");
|
|
1223
1247
|
const [inviteWarning, setInviteWarning] = useState12(null);
|
|
1224
1248
|
const [cooldown, setCooldown] = useState12(0);
|
|
1225
1249
|
const [resendEmail, setResendEmail] = useState12("");
|
|
1226
1250
|
const [resendSent, setResendSent] = useState12(false);
|
|
1251
|
+
const [isSigningOut, setIsSigningOut] = useState12(false);
|
|
1227
1252
|
const verifiedRef = useRef4(false);
|
|
1228
1253
|
const sessionEmail = session.data?.user?.email ?? null;
|
|
1229
1254
|
useEffect8(() => {
|
|
@@ -1278,6 +1303,15 @@ function VerifyEmailPage() {
|
|
|
1278
1303
|
setResendSent(true);
|
|
1279
1304
|
setCooldown(60);
|
|
1280
1305
|
}, [sessionEmail, resendEmail, cooldown, sendVerificationEmail]);
|
|
1306
|
+
const handleBackToSignIn = useCallback3(async () => {
|
|
1307
|
+
if (isSigningOut) return;
|
|
1308
|
+
setIsSigningOut(true);
|
|
1309
|
+
try {
|
|
1310
|
+
await signOut();
|
|
1311
|
+
} finally {
|
|
1312
|
+
navigateToSignIn();
|
|
1313
|
+
}
|
|
1314
|
+
}, [isSigningOut, signOut]);
|
|
1281
1315
|
const resendButton = /* @__PURE__ */ jsx12(
|
|
1282
1316
|
Button7,
|
|
1283
1317
|
{
|
|
@@ -1329,17 +1363,38 @@ function VerifyEmailPage() {
|
|
|
1329
1363
|
/* @__PURE__ */ jsx12(CardDescription5, { children: "This verification link is no longer valid. Request a new one below." })
|
|
1330
1364
|
] }),
|
|
1331
1365
|
/* @__PURE__ */ jsx12(CardContent5, { children: resendSection }),
|
|
1332
|
-
/* @__PURE__ */ jsx12(CardFooter5, { children: /* @__PURE__ */ jsx12(
|
|
1366
|
+
/* @__PURE__ */ jsx12(CardFooter5, { children: /* @__PURE__ */ jsx12(
|
|
1367
|
+
Button7,
|
|
1368
|
+
{
|
|
1369
|
+
type: "button",
|
|
1370
|
+
variant: "link",
|
|
1371
|
+
className: "h-auto p-0 text-sm text-muted-foreground hover:underline",
|
|
1372
|
+
disabled: isSigningOut,
|
|
1373
|
+
onClick: handleBackToSignIn,
|
|
1374
|
+
children: isSigningOut ? "Signing out\u2026" : "Back to sign in"
|
|
1375
|
+
}
|
|
1376
|
+
) })
|
|
1333
1377
|
] }) });
|
|
1334
1378
|
}
|
|
1379
|
+
const isWaitingForEmailClick = status === "no-token" && Boolean(sessionEmail);
|
|
1335
1380
|
return /* @__PURE__ */ jsx12("div", { className: "flex min-h-screen items-center justify-center p-4", children: /* @__PURE__ */ jsxs5(Card5, { className: "w-full max-w-sm", children: [
|
|
1336
1381
|
inviteWarning && /* @__PURE__ */ jsx12("div", { role: "status", className: "px-6 pt-4", children: /* @__PURE__ */ jsx12("div", { className: "rounded-md bg-muted px-3 py-2 text-sm text-muted-foreground", children: inviteWarning }) }),
|
|
1337
1382
|
/* @__PURE__ */ jsxs5(CardHeader5, { children: [
|
|
1338
|
-
/* @__PURE__ */ jsx12(CardTitle5, { children: "Invalid verification link" }),
|
|
1339
|
-
/* @__PURE__ */ jsx12(CardDescription5, { children: status === "no-token" ? "No verification token found. Check the link in your email." : "This verification link is invalid. Request a new one below." })
|
|
1383
|
+
/* @__PURE__ */ jsx12(CardTitle5, { children: isWaitingForEmailClick ? "Check your email" : "Invalid verification link" }),
|
|
1384
|
+
/* @__PURE__ */ jsx12(CardDescription5, { children: isWaitingForEmailClick ? "We sent a verification link to your email address. Please check your inbox to continue." : status === "no-token" ? "No verification token found. Check the link in your email." : "This verification link is invalid. Request a new one below." })
|
|
1340
1385
|
] }),
|
|
1341
1386
|
/* @__PURE__ */ jsx12(CardContent5, { children: resendSection }),
|
|
1342
|
-
/* @__PURE__ */ jsx12(CardFooter5, { children: /* @__PURE__ */ jsx12(
|
|
1387
|
+
/* @__PURE__ */ jsx12(CardFooter5, { children: /* @__PURE__ */ jsx12(
|
|
1388
|
+
Button7,
|
|
1389
|
+
{
|
|
1390
|
+
type: "button",
|
|
1391
|
+
variant: "link",
|
|
1392
|
+
className: "h-auto p-0 text-sm text-muted-foreground hover:underline",
|
|
1393
|
+
disabled: isSigningOut,
|
|
1394
|
+
onClick: handleBackToSignIn,
|
|
1395
|
+
children: isSigningOut ? "Signing out\u2026" : "Back to sign in"
|
|
1396
|
+
}
|
|
1397
|
+
) })
|
|
1343
1398
|
] }) });
|
|
1344
1399
|
}
|
|
1345
1400
|
|
|
@@ -1399,6 +1454,9 @@ function formatMemberSince(value) {
|
|
|
1399
1454
|
});
|
|
1400
1455
|
}
|
|
1401
1456
|
function SettingsTopBar() {
|
|
1457
|
+
const config = useOptionalConfig();
|
|
1458
|
+
const appTitle = config?.appName ?? "Boring UI";
|
|
1459
|
+
const appInitial = (appTitle.trim().charAt(0) || "B").toUpperCase();
|
|
1402
1460
|
return /* @__PURE__ */ jsx13(
|
|
1403
1461
|
"header",
|
|
1404
1462
|
{
|
|
@@ -1410,10 +1468,10 @@ function SettingsTopBar() {
|
|
|
1410
1468
|
{
|
|
1411
1469
|
"aria-hidden": "true",
|
|
1412
1470
|
className: "flex h-7 w-7 shrink-0 items-center justify-center rounded-md bg-foreground text-[12px] font-semibold text-background",
|
|
1413
|
-
children:
|
|
1471
|
+
children: appInitial
|
|
1414
1472
|
}
|
|
1415
1473
|
),
|
|
1416
|
-
/* @__PURE__ */ jsx13("span", { className: "truncate text-[13px] font-medium tracking-tight text-foreground", children:
|
|
1474
|
+
/* @__PURE__ */ jsx13("span", { className: "truncate text-[13px] font-medium tracking-tight text-foreground", children: appTitle }),
|
|
1417
1475
|
/* @__PURE__ */ jsx13("span", { "aria-hidden": "true", className: "text-muted-foreground/30", children: "/" }),
|
|
1418
1476
|
/* @__PURE__ */ jsx13("span", { className: "truncate text-[13px] text-muted-foreground", children: "Account settings" })
|
|
1419
1477
|
] })
|
|
@@ -1740,7 +1798,7 @@ function UserSettingsPage({ topBar, extraSections = [] } = {}) {
|
|
|
1740
1798
|
|
|
1741
1799
|
// src/front/auth/InviteAcceptPage.tsx
|
|
1742
1800
|
import { useCallback as useCallback5, useState as useState14 } from "react";
|
|
1743
|
-
import { useParams as useParams2, useNavigate } from "react-router-dom";
|
|
1801
|
+
import { useParams as useParams2, useNavigate as useNavigate2 } from "react-router-dom";
|
|
1744
1802
|
import { useQuery as useQuery3, useMutation, useQueryClient as useQueryClient2 } from "@tanstack/react-query";
|
|
1745
1803
|
import {
|
|
1746
1804
|
Button as Button9,
|
|
@@ -1757,7 +1815,7 @@ import { jsx as jsx14, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
|
1757
1815
|
function InviteAcceptPage() {
|
|
1758
1816
|
const { token } = useParams2();
|
|
1759
1817
|
const session = useSession();
|
|
1760
|
-
const navigate =
|
|
1818
|
+
const navigate = useNavigate2();
|
|
1761
1819
|
const queryClient = useQueryClient2();
|
|
1762
1820
|
const [acceptError, setAcceptError] = useState14(null);
|
|
1763
1821
|
const isSignedIn = Boolean(session.data);
|
|
@@ -1891,6 +1949,15 @@ import { matchPath as matchPath2 } from "react-router-dom";
|
|
|
1891
1949
|
import { Fragment as Fragment3, jsx as jsx15 } from "react/jsx-runtime";
|
|
1892
1950
|
var DEFAULT_GRACE_MS = 3e4;
|
|
1893
1951
|
var UNSAFE_REDIRECT_RE = /[\0\r\n<>"'`]/;
|
|
1952
|
+
var UNVERIFIED_ALLOWED_PATHS = /* @__PURE__ */ new Set([
|
|
1953
|
+
routes.signup,
|
|
1954
|
+
routes.forgotPassword,
|
|
1955
|
+
routes.resetPassword,
|
|
1956
|
+
routes.verifyEmail,
|
|
1957
|
+
routes.authError,
|
|
1958
|
+
routes.callbackGithub,
|
|
1959
|
+
routes.callbackGoogle
|
|
1960
|
+
]);
|
|
1894
1961
|
function normalizePath(pathname) {
|
|
1895
1962
|
if (!pathname) return "/";
|
|
1896
1963
|
return pathname.startsWith("/") ? pathname : `/${pathname}`;
|
|
@@ -1921,6 +1988,9 @@ function isPublicPath(pathname, publicPaths) {
|
|
|
1921
1988
|
return normalizedPath === candidate || normalizedPath.startsWith(`${candidate}/`);
|
|
1922
1989
|
});
|
|
1923
1990
|
}
|
|
1991
|
+
function shouldBlockUnverifiedUser(pathname, hasSession, requireEmailVerification, isEmailVerified) {
|
|
1992
|
+
return hasSession && requireEmailVerification && !isEmailVerified && !UNVERIFIED_ALLOWED_PATHS.has(normalizePath(pathname));
|
|
1993
|
+
}
|
|
1924
1994
|
function readSafeRedirect(search) {
|
|
1925
1995
|
const redirect = new URLSearchParams(normalizeSearch(search)).get("redirect");
|
|
1926
1996
|
if (!redirect) return null;
|
|
@@ -1950,7 +2020,8 @@ function AuthGate({
|
|
|
1950
2020
|
graceMs = DEFAULT_GRACE_MS,
|
|
1951
2021
|
location,
|
|
1952
2022
|
navigate,
|
|
1953
|
-
now
|
|
2023
|
+
now,
|
|
2024
|
+
requireEmailVerification = false
|
|
1954
2025
|
}) {
|
|
1955
2026
|
const session = useSession();
|
|
1956
2027
|
const nullSinceRef = useRef5(null);
|
|
@@ -1962,6 +2033,7 @@ function AuthGate({
|
|
|
1962
2033
|
() => publicPaths.map(normalizePublicPath),
|
|
1963
2034
|
[publicPaths]
|
|
1964
2035
|
);
|
|
2036
|
+
const isEmailVerified = session.data?.user?.emailVerified ?? false;
|
|
1965
2037
|
useEffect9(() => {
|
|
1966
2038
|
return () => {
|
|
1967
2039
|
if (!redirectTimerRef.current) return;
|
|
@@ -1978,6 +2050,10 @@ function AuthGate({
|
|
|
1978
2050
|
const currentPath = buildCurrentPath(currentLocation);
|
|
1979
2051
|
if (session.data) {
|
|
1980
2052
|
nullSinceRef.current = null;
|
|
2053
|
+
if (shouldBlockUnverifiedUser(pathname2, true, requireEmailVerification, isEmailVerified)) {
|
|
2054
|
+
goTo(routes.verifyEmail, { replace: true });
|
|
2055
|
+
return;
|
|
2056
|
+
}
|
|
1981
2057
|
if (pathname2 === routes.signin) {
|
|
1982
2058
|
const destination = readSafeRedirect(currentLocation.search) ?? "/";
|
|
1983
2059
|
if (destination !== currentPath) {
|
|
@@ -2008,15 +2084,16 @@ function AuthGate({
|
|
|
2008
2084
|
goTo(`${routes.signin}?redirect=${encodeURIComponent(currentPath)}`, { replace: true });
|
|
2009
2085
|
}, remainingMs);
|
|
2010
2086
|
redirectTimerRef.current.unref?.();
|
|
2011
|
-
}, [currentLocation, goTo, graceMs, normalizedPublicPaths, readNow, session.data, session.isPending]);
|
|
2087
|
+
}, [currentLocation, goTo, graceMs, isEmailVerified, normalizedPublicPaths, readNow, requireEmailVerification, session.data, session.isPending]);
|
|
2012
2088
|
const pathname = normalizePath(currentLocation.pathname);
|
|
2089
|
+
if (shouldBlockUnverifiedUser(pathname, Boolean(session.data), requireEmailVerification, isEmailVerified)) return null;
|
|
2013
2090
|
if (session.isPending && !isPublicPath(pathname, normalizedPublicPaths)) return null;
|
|
2014
2091
|
return /* @__PURE__ */ jsx15(Fragment3, { children });
|
|
2015
2092
|
}
|
|
2016
2093
|
|
|
2017
2094
|
// src/front/CoreFront.tsx
|
|
2018
2095
|
import { Suspense, useCallback as useCallback9, useMemo as useMemo6 } from "react";
|
|
2019
|
-
import { BrowserRouter, Routes, Route, useLocation as useLocation2, useNavigate as
|
|
2096
|
+
import { BrowserRouter, Routes, Route, useLocation as useLocation2, useNavigate as useNavigate6 } from "react-router-dom";
|
|
2020
2097
|
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
|
2021
2098
|
import { Helmet, HelmetProvider } from "react-helmet-async";
|
|
2022
2099
|
|
|
@@ -2040,7 +2117,7 @@ import {
|
|
|
2040
2117
|
Settings,
|
|
2041
2118
|
Sun
|
|
2042
2119
|
} from "lucide-react";
|
|
2043
|
-
import { useNavigate as
|
|
2120
|
+
import { useNavigate as useNavigate3 } from "react-router-dom";
|
|
2044
2121
|
import { jsx as jsx16, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
2045
2122
|
var THEME_ORDER = ["light", "dark", "system"];
|
|
2046
2123
|
function labelForTheme(preference) {
|
|
@@ -2062,7 +2139,7 @@ function initialsFor2(name, email) {
|
|
|
2062
2139
|
function UserMenu() {
|
|
2063
2140
|
const identity = useUser();
|
|
2064
2141
|
const signOut = useSignOut();
|
|
2065
|
-
const navigate =
|
|
2142
|
+
const navigate = useNavigate3();
|
|
2066
2143
|
const { preference, setTheme } = useTheme();
|
|
2067
2144
|
const [isSigningOut, setIsSigningOut] = useState15(false);
|
|
2068
2145
|
const user = identity?.user;
|
|
@@ -2192,7 +2269,7 @@ import {
|
|
|
2192
2269
|
useToast
|
|
2193
2270
|
} from "@hachej/boring-ui-kit";
|
|
2194
2271
|
import { ChevronsUpDown, LayoutGrid, Plus, Settings as Settings2 } from "lucide-react";
|
|
2195
|
-
import { useNavigate as
|
|
2272
|
+
import { useNavigate as useNavigate4 } from "react-router-dom";
|
|
2196
2273
|
import { z as z6 } from "zod";
|
|
2197
2274
|
import { Fragment as Fragment4, jsx as jsx17, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
2198
2275
|
var workspaceNameSchema = z6.object({
|
|
@@ -2238,10 +2315,12 @@ function OpenInNewTabIcon({ className }) {
|
|
|
2238
2315
|
] });
|
|
2239
2316
|
}
|
|
2240
2317
|
function WorkspaceSwitcher({
|
|
2241
|
-
appTitle
|
|
2318
|
+
appTitle,
|
|
2242
2319
|
workspacePathPrefix = "/workspace"
|
|
2243
2320
|
}) {
|
|
2244
|
-
const
|
|
2321
|
+
const config = useOptionalConfig();
|
|
2322
|
+
const resolvedAppTitle = appTitle ?? config?.appName ?? "Boring UI";
|
|
2323
|
+
const navigate = useNavigate4();
|
|
2245
2324
|
const queryClient = useQueryClient3();
|
|
2246
2325
|
const { toast } = useToastCompat();
|
|
2247
2326
|
const currentWorkspace = useCurrentWorkspace();
|
|
@@ -2328,7 +2407,7 @@ function WorkspaceSwitcher({
|
|
|
2328
2407
|
{
|
|
2329
2408
|
"aria-hidden": "true",
|
|
2330
2409
|
className: "flex h-7 w-7 shrink-0 items-center justify-center rounded-md bg-foreground text-[12px] font-semibold text-background",
|
|
2331
|
-
children:
|
|
2410
|
+
children: resolvedAppTitle.charAt(0).toUpperCase()
|
|
2332
2411
|
}
|
|
2333
2412
|
),
|
|
2334
2413
|
/* @__PURE__ */ jsx17("span", { className: "text-[13px] font-medium text-foreground", children: "Create your first workspace" })
|
|
@@ -2348,11 +2427,11 @@ function WorkspaceSwitcher({
|
|
|
2348
2427
|
{
|
|
2349
2428
|
"aria-hidden": "true",
|
|
2350
2429
|
className: "flex h-7 w-7 shrink-0 items-center justify-center rounded-md bg-foreground text-[12px] font-semibold text-background",
|
|
2351
|
-
children:
|
|
2430
|
+
children: resolvedAppTitle.charAt(0).toUpperCase()
|
|
2352
2431
|
}
|
|
2353
2432
|
),
|
|
2354
2433
|
/* @__PURE__ */ jsxs9("span", { className: "flex min-w-0 items-center gap-1.5", children: [
|
|
2355
|
-
/* @__PURE__ */ jsx17("span", { className: "truncate text-[13px] font-medium text-foreground", children:
|
|
2434
|
+
/* @__PURE__ */ jsx17("span", { className: "truncate text-[13px] font-medium text-foreground", children: resolvedAppTitle }),
|
|
2356
2435
|
/* @__PURE__ */ jsx17("span", { "aria-hidden": "true", className: "text-muted-foreground/30", children: "/" }),
|
|
2357
2436
|
/* @__PURE__ */ jsx17("span", { className: "truncate text-[13px] font-normal text-muted-foreground", children: switcherLabel })
|
|
2358
2437
|
] }),
|
|
@@ -2985,7 +3064,7 @@ function MembersPage() {
|
|
|
2985
3064
|
// src/front/workspace/WorkspaceSettingsPage.tsx
|
|
2986
3065
|
import { useCallback as useCallback8, useEffect as useEffect10, useState as useState19 } from "react";
|
|
2987
3066
|
import { useQuery as useQuery6, useMutation as useMutation4, useQueryClient as useQueryClient6 } from "@tanstack/react-query";
|
|
2988
|
-
import { useNavigate as
|
|
3067
|
+
import { useNavigate as useNavigate5 } from "react-router-dom";
|
|
2989
3068
|
import {
|
|
2990
3069
|
AlertDialog as AlertDialog3,
|
|
2991
3070
|
AlertDialogCancel as AlertDialogCancel3,
|
|
@@ -3020,7 +3099,10 @@ var STATE_TONES = {
|
|
|
3020
3099
|
error: "danger"
|
|
3021
3100
|
};
|
|
3022
3101
|
function SettingsTopBar2({ workspaceId, workspaceName }) {
|
|
3023
|
-
const
|
|
3102
|
+
const config = useOptionalConfig();
|
|
3103
|
+
const appTitle = config?.appName ?? "Boring UI";
|
|
3104
|
+
const appInitial = (appTitle.trim().charAt(0) || "B").toUpperCase();
|
|
3105
|
+
const navigate = useNavigate5();
|
|
3024
3106
|
const workspaceHref = workspaceId ? `/workspace/${encodeURIComponent(workspaceId)}` : "/";
|
|
3025
3107
|
return /* @__PURE__ */ jsx21(
|
|
3026
3108
|
"header",
|
|
@@ -3038,10 +3120,10 @@ function SettingsTopBar2({ workspaceId, workspaceName }) {
|
|
|
3038
3120
|
title: "Back to workspace",
|
|
3039
3121
|
onClick: () => navigate(workspaceHref),
|
|
3040
3122
|
className: "shrink-0 bg-foreground text-[12px] font-semibold text-background hover:bg-foreground/90",
|
|
3041
|
-
children:
|
|
3123
|
+
children: appInitial
|
|
3042
3124
|
}
|
|
3043
3125
|
),
|
|
3044
|
-
/* @__PURE__ */ jsx21("span", { className: "truncate text-[13px] font-medium tracking-tight text-foreground", children:
|
|
3126
|
+
/* @__PURE__ */ jsx21("span", { className: "truncate text-[13px] font-medium tracking-tight text-foreground", children: appTitle }),
|
|
3045
3127
|
/* @__PURE__ */ jsx21("span", { "aria-hidden": "true", className: "text-muted-foreground/30", children: "/" }),
|
|
3046
3128
|
/* @__PURE__ */ jsx21("span", { className: "truncate text-[13px] text-muted-foreground", children: workspaceName }),
|
|
3047
3129
|
/* @__PURE__ */ jsx21("span", { "aria-hidden": "true", className: "text-muted-foreground/30", children: "/" }),
|
|
@@ -3091,7 +3173,7 @@ function WorkspaceSettingsPage({ topBar } = {}) {
|
|
|
3091
3173
|
const workspace = useCurrentWorkspace();
|
|
3092
3174
|
const role = useWorkspaceRole();
|
|
3093
3175
|
const queryClient = useQueryClient6();
|
|
3094
|
-
const navigate =
|
|
3176
|
+
const navigate = useNavigate5();
|
|
3095
3177
|
const workspaceId = workspace?.id ?? "";
|
|
3096
3178
|
const [nameValue, setNameValue] = useState19(null);
|
|
3097
3179
|
const [nameError, setNameError] = useState19(null);
|
|
@@ -3533,8 +3615,9 @@ function createDefaultQueryClient() {
|
|
|
3533
3615
|
});
|
|
3534
3616
|
}
|
|
3535
3617
|
function RouterAuthGate({ children, publicPaths }) {
|
|
3618
|
+
const config = useConfig();
|
|
3536
3619
|
const location = useLocation2();
|
|
3537
|
-
const navigate =
|
|
3620
|
+
const navigate = useNavigate6();
|
|
3538
3621
|
const authLocation = useMemo6(
|
|
3539
3622
|
() => ({ pathname: location.pathname, search: location.search, hash: location.hash }),
|
|
3540
3623
|
[location.hash, location.pathname, location.search]
|
|
@@ -3551,6 +3634,7 @@ function RouterAuthGate({ children, publicPaths }) {
|
|
|
3551
3634
|
location: authLocation,
|
|
3552
3635
|
navigate: navigateWithinRouter,
|
|
3553
3636
|
publicPaths,
|
|
3637
|
+
requireEmailVerification: isRuntimeEmailVerificationEnabled(config),
|
|
3554
3638
|
children
|
|
3555
3639
|
}
|
|
3556
3640
|
);
|
|
@@ -3605,7 +3689,7 @@ function CoreFront({ children, authPages, cspNonce, workspaceRoute, workspaceIdP
|
|
|
3605
3689
|
|
|
3606
3690
|
// src/front/commands/CoreCommandContributions.tsx
|
|
3607
3691
|
import { useMemo as useMemo7, useState as useState20 } from "react";
|
|
3608
|
-
import { useNavigate as
|
|
3692
|
+
import { useNavigate as useNavigate7 } from "react-router-dom";
|
|
3609
3693
|
|
|
3610
3694
|
// src/front/workspace/commands.ts
|
|
3611
3695
|
function getWorkspaceCommands(workspaceId, navigate) {
|
|
@@ -3643,7 +3727,7 @@ function toPaletteCommand(command) {
|
|
|
3643
3727
|
};
|
|
3644
3728
|
}
|
|
3645
3729
|
function useCoreCommands() {
|
|
3646
|
-
const navigate =
|
|
3730
|
+
const navigate = useNavigate7();
|
|
3647
3731
|
const signOut = useSignOut();
|
|
3648
3732
|
const workspace = useCurrentWorkspace();
|
|
3649
3733
|
const [isSigningOut, setIsSigningOut] = useState20(false);
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// src/shared/authPolicy.ts
|
|
2
|
+
function isCoreEmailVerificationEnabled(config) {
|
|
3
|
+
return Boolean(config.auth.mail);
|
|
4
|
+
}
|
|
5
|
+
function isRuntimeEmailVerificationEnabled(config) {
|
|
6
|
+
return config?.features.emailVerification === true;
|
|
7
|
+
}
|
|
8
|
+
function canUseProtectedApi(user, requireEmailVerification) {
|
|
9
|
+
if (!user) return false;
|
|
10
|
+
return !requireEmailVerification || user.emailVerified === true;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export {
|
|
14
|
+
isCoreEmailVerificationEnabled,
|
|
15
|
+
isRuntimeEmailVerificationEnabled,
|
|
16
|
+
canUseProtectedApi
|
|
17
|
+
};
|
|
@@ -12,16 +12,19 @@ import {
|
|
|
12
12
|
workspaceRuntimes,
|
|
13
13
|
workspaceSettings,
|
|
14
14
|
workspaces
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-BVZ2YT3M.js";
|
|
16
16
|
import {
|
|
17
17
|
noopTelemetry,
|
|
18
18
|
safeCapture
|
|
19
19
|
} from "./chunk-AQBXNPMD.js";
|
|
20
|
+
import {
|
|
21
|
+
isCoreEmailVerificationEnabled
|
|
22
|
+
} from "./chunk-I4PGL4ZD.js";
|
|
20
23
|
import {
|
|
21
24
|
ConfigValidationError,
|
|
22
25
|
ERROR_CODES,
|
|
23
26
|
HttpError
|
|
24
|
-
} from "./chunk-
|
|
27
|
+
} from "./chunk-QZGYKLXB.js";
|
|
25
28
|
|
|
26
29
|
// src/server/config/schema.ts
|
|
27
30
|
import { z } from "zod";
|
|
@@ -290,7 +293,8 @@ function buildRuntimeConfigPayload(config) {
|
|
|
290
293
|
githubOauth: config.features.githubOauth,
|
|
291
294
|
googleOauth: isGoogleOauthUsable(config),
|
|
292
295
|
invitesEnabled: config.features.invitesEnabled,
|
|
293
|
-
sendWelcomeEmail: config.features.sendWelcomeEmail
|
|
296
|
+
sendWelcomeEmail: config.features.sendWelcomeEmail,
|
|
297
|
+
emailVerification: isCoreEmailVerificationEnabled(config)
|
|
294
298
|
}
|
|
295
299
|
};
|
|
296
300
|
}
|
|
@@ -419,7 +423,7 @@ function registerCapabilities(app) {
|
|
|
419
423
|
contributors.set(name, fn);
|
|
420
424
|
}
|
|
421
425
|
);
|
|
422
|
-
const
|
|
426
|
+
const emailVerificationEnabled = isCoreEmailVerificationEnabled(app.config);
|
|
423
427
|
app.registerCapabilitiesContributor("core", () => {
|
|
424
428
|
const googleOauth = isGoogleOauthUsable(app.config);
|
|
425
429
|
const core = {
|
|
@@ -428,15 +432,15 @@ function registerCapabilities(app) {
|
|
|
428
432
|
invitesEnabled: app.config.features.invitesEnabled,
|
|
429
433
|
githubOauth: app.config.features.githubOauth,
|
|
430
434
|
googleOauth,
|
|
431
|
-
emailFlows:
|
|
435
|
+
emailFlows: emailVerificationEnabled
|
|
432
436
|
},
|
|
433
437
|
auth: {
|
|
434
438
|
emailPassword: true,
|
|
435
439
|
github: false,
|
|
436
440
|
google: googleOauth,
|
|
437
|
-
emailVerification:
|
|
438
|
-
passwordReset:
|
|
439
|
-
magicLink:
|
|
441
|
+
emailVerification: emailVerificationEnabled,
|
|
442
|
+
passwordReset: emailVerificationEnabled,
|
|
443
|
+
magicLink: emailVerificationEnabled
|
|
440
444
|
}
|
|
441
445
|
};
|
|
442
446
|
return { core };
|
|
@@ -1747,7 +1751,8 @@ function buildMailTransport(config) {
|
|
|
1747
1751
|
function createAuth(config, db, opts) {
|
|
1748
1752
|
const transport = buildMailTransport(config);
|
|
1749
1753
|
const telemetry = opts?.telemetry ?? noopTelemetry;
|
|
1750
|
-
const
|
|
1754
|
+
const emailVerificationEnabled = isCoreEmailVerificationEnabled(config);
|
|
1755
|
+
const emailVerificationConfig = emailVerificationEnabled && transport ? {
|
|
1751
1756
|
sendOnSignUp: true,
|
|
1752
1757
|
sendVerificationEmail: async (data) => {
|
|
1753
1758
|
const email = await renderVerifyEmail({
|
|
@@ -1930,7 +1935,8 @@ var authHookPlugin = async (app, opts) => {
|
|
|
1930
1935
|
request.user = {
|
|
1931
1936
|
id: result.user.id,
|
|
1932
1937
|
email: result.user.email,
|
|
1933
|
-
name: result.user.name ?? null
|
|
1938
|
+
name: result.user.name ?? null,
|
|
1939
|
+
emailVerified: Boolean(result.user.emailVerified)
|
|
1934
1940
|
};
|
|
1935
1941
|
}
|
|
1936
1942
|
} catch {
|
|
@@ -1938,7 +1944,8 @@ var authHookPlugin = async (app, opts) => {
|
|
|
1938
1944
|
}
|
|
1939
1945
|
}
|
|
1940
1946
|
const path = request.url.split("?")[0];
|
|
1941
|
-
|
|
1947
|
+
const isProtectedApi = path.startsWith("/api/v1/") && !publicPatterns.some((re) => re.test(path));
|
|
1948
|
+
if (isProtectedApi && !request.user) {
|
|
1942
1949
|
throw new HttpError({
|
|
1943
1950
|
status: 401,
|
|
1944
1951
|
code: ERROR_CODES.UNAUTHORIZED,
|
|
@@ -1946,6 +1953,14 @@ var authHookPlugin = async (app, opts) => {
|
|
|
1946
1953
|
requestId: request.id
|
|
1947
1954
|
});
|
|
1948
1955
|
}
|
|
1956
|
+
if (isProtectedApi && isCoreEmailVerificationEnabled(app.config) && request.user?.emailVerified !== true) {
|
|
1957
|
+
throw new HttpError({
|
|
1958
|
+
status: 403,
|
|
1959
|
+
code: ERROR_CODES.EMAIL_NOT_VERIFIED,
|
|
1960
|
+
message: "Email verification required",
|
|
1961
|
+
requestId: request.id
|
|
1962
|
+
});
|
|
1963
|
+
}
|
|
1949
1964
|
});
|
|
1950
1965
|
};
|
|
1951
1966
|
var authHook = fp2(authHookPlugin, { name: "auth-hook" });
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { C as CoreConfig,
|
|
1
|
+
import { C as CoreConfig, c as Workspace, E as ERROR_CODES, M as MemberRole, d as WorkspaceMember, U as User, e as WorkspaceInvite, f as WorkspaceRuntime, W as WorkspaceRuntimeResourceSelector, a as WorkspaceRuntimeResource, b as WorkspaceRuntimeResourceInput, g as CapabilitiesResponse } from './types-Bb7I-83I.js';
|
|
2
2
|
import { drizzle } from 'drizzle-orm/postgres-js';
|
|
3
3
|
import postgres from 'postgres';
|
|
4
4
|
|
|
@@ -131,6 +131,7 @@ declare module 'fastify' {
|
|
|
131
131
|
id: string;
|
|
132
132
|
email: string;
|
|
133
133
|
name: string | null;
|
|
134
|
+
emailVerified: boolean;
|
|
134
135
|
} | null;
|
|
135
136
|
cspNonce?: string;
|
|
136
137
|
}
|
|
@@ -142,4 +143,4 @@ declare function createDatabase(config: CoreConfig): {
|
|
|
142
143
|
sql: postgres.Sql;
|
|
143
144
|
};
|
|
144
145
|
|
|
145
|
-
export { type AuthProvider as A, type CreateCoreAppOptions as C, type Database as D, type ProvisionContext as P, type UserStore as U, type WorkspaceStore as W, type WorkspaceProvisioner as a, type CapabilitiesContributor as b,
|
|
146
|
+
export { type AuthProvider as A, type CreateCoreAppOptions as C, type Database as D, type ProvisionContext as P, type UserStore as U, type WorkspaceStore as W, type WorkspaceProvisioner as a, type CapabilitiesContributor as b, type ProvisionResult as c, createDatabase as d };
|
package/dist/front/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
2
|
import * as react from 'react';
|
|
3
3
|
import { Component, ReactNode, ErrorInfo } from 'react';
|
|
4
|
-
import { R as RuntimeConfig, g as CapabilitiesResponse,
|
|
4
|
+
import { R as RuntimeConfig, g as CapabilitiesResponse, d as WorkspaceMember, U as User, c as Workspace, M as MemberRole, S as SessionState } from '../types-Bb7I-83I.js';
|
|
5
5
|
import * as _tanstack_react_query from '@tanstack/react-query';
|
|
6
6
|
import * as better_auth_react from 'better-auth/react';
|
|
7
7
|
import { createAuthClient } from 'better-auth/react';
|
|
@@ -461,8 +461,9 @@ interface AuthGateProps {
|
|
|
461
461
|
replace?: boolean;
|
|
462
462
|
}) => void;
|
|
463
463
|
now?: () => number;
|
|
464
|
+
requireEmailVerification?: boolean;
|
|
464
465
|
}
|
|
465
|
-
declare function AuthGate({ children, publicPaths, graceMs, location, navigate, now, }: AuthGateProps): react_jsx_runtime.JSX.Element | null;
|
|
466
|
+
declare function AuthGate({ children, publicPaths, graceMs, location, navigate, now, requireEmailVerification, }: AuthGateProps): react_jsx_runtime.JSX.Element | null;
|
|
466
467
|
|
|
467
468
|
interface CoreCommand {
|
|
468
469
|
id: string;
|
package/dist/front/index.js
CHANGED
|
@@ -58,12 +58,13 @@ import {
|
|
|
58
58
|
useWorkspaceMembers,
|
|
59
59
|
useWorkspaceRole,
|
|
60
60
|
useWorkspaceRouteStatus
|
|
61
|
-
} from "../chunk-
|
|
61
|
+
} from "../chunk-2ZTKRLVG.js";
|
|
62
62
|
import {
|
|
63
63
|
TopBarSlotProvider,
|
|
64
64
|
useTopBarSlot
|
|
65
65
|
} from "../chunk-HYNKZSTF.js";
|
|
66
|
-
import "../chunk-
|
|
66
|
+
import "../chunk-I4PGL4ZD.js";
|
|
67
|
+
import "../chunk-QZGYKLXB.js";
|
|
67
68
|
import "../chunk-MLKGABMK.js";
|
|
68
69
|
export {
|
|
69
70
|
AppErrorBoundary,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { U as UserStore, W as WorkspaceStore } from '../../connection-
|
|
2
|
-
export { D as Database,
|
|
3
|
-
export { F as FinishReservationInput, G as GrantOnceInput, I as InsufficientCreditError, M as MeteringBalance, P as PostgresMeteringStore, R as RecordUsageInput, a as RecordUsageResult, b as ReservationFinalStatus, c as ReserveInput, d as ReserveResult, e as RunMigrationsOptions, r as runMigrations } from '../../PostgresMeteringStore-
|
|
4
|
-
import { U as User,
|
|
1
|
+
import { U as UserStore, W as WorkspaceStore } from '../../connection-B1iC6B-w.js';
|
|
2
|
+
export { D as Database, d as createDatabase } from '../../connection-B1iC6B-w.js';
|
|
3
|
+
export { F as FinishReservationInput, G as GrantOnceInput, I as InsufficientCreditError, M as MeteringBalance, P as PostgresMeteringStore, R as RecordUsageInput, a as RecordUsageResult, b as ReservationFinalStatus, c as ReserveInput, d as ReserveResult, e as RunMigrationsOptions, r as runMigrations } from '../../PostgresMeteringStore-qjKGmVFr.js';
|
|
4
|
+
import { U as User, c as Workspace, E as ERROR_CODES, M as MemberRole, d as WorkspaceMember, e as WorkspaceInvite, f as WorkspaceRuntime, W as WorkspaceRuntimeResourceSelector, a as WorkspaceRuntimeResource, b as WorkspaceRuntimeResourceInput } from '../../types-Bb7I-83I.js';
|
|
5
5
|
import { PostgresJsDatabase } from 'drizzle-orm/postgres-js';
|
|
6
6
|
import 'postgres';
|
|
7
7
|
|
package/dist/server/db/index.js
CHANGED
|
@@ -7,8 +7,8 @@ import {
|
|
|
7
7
|
PostgresWorkspaceStore,
|
|
8
8
|
createDatabase,
|
|
9
9
|
runMigrations
|
|
10
|
-
} from "../../chunk-
|
|
11
|
-
import "../../chunk-
|
|
10
|
+
} from "../../chunk-BVZ2YT3M.js";
|
|
11
|
+
import "../../chunk-QZGYKLXB.js";
|
|
12
12
|
import "../../chunk-MLKGABMK.js";
|
|
13
13
|
export {
|
|
14
14
|
InsufficientCreditError,
|