feat(db): mesh data model — meshes, members, invites, audit log
- pgSchema "mesh" with 4 tables isolating the peer mesh domain - Enums: visibility, transport, tier, role - audit_log is metadata-only (E2E encryption enforced at broker/client) - Cascade on mesh delete, soft-delete via archivedAt/revokedAt Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
30
apps/web/src/lib/providers/analytics.tsx
Normal file
30
apps/web/src/lib/providers/analytics.tsx
Normal file
@@ -0,0 +1,30 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect } from "react";
|
||||
|
||||
import { identify, Provider, reset } from "@turbostarter/analytics-web";
|
||||
|
||||
import { authClient } from "~/lib/auth/client";
|
||||
|
||||
export const AnalyticsProvider = ({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
}) => {
|
||||
const session = authClient.useSession();
|
||||
|
||||
useEffect(() => {
|
||||
if (session.isPending) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (session.data?.user) {
|
||||
const { id, email, name } = session.data.user;
|
||||
identify(id, { email, name });
|
||||
} else {
|
||||
reset();
|
||||
}
|
||||
}, [session]);
|
||||
|
||||
return <Provider>{children}</Provider>;
|
||||
};
|
||||
25
apps/web/src/lib/providers/monitoring.tsx
Normal file
25
apps/web/src/lib/providers/monitoring.tsx
Normal file
@@ -0,0 +1,25 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect } from "react";
|
||||
|
||||
import { identify } from "@turbostarter/monitoring-web";
|
||||
|
||||
import { authClient } from "~/lib/auth/client";
|
||||
|
||||
export const MonitoringProvider = ({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
}) => {
|
||||
const session = authClient.useSession();
|
||||
|
||||
useEffect(() => {
|
||||
if (session.isPending) {
|
||||
return;
|
||||
}
|
||||
|
||||
identify(session.data?.user ?? null);
|
||||
}, [session]);
|
||||
|
||||
return <>{children}</>;
|
||||
};
|
||||
34
apps/web/src/lib/providers/providers.tsx
Normal file
34
apps/web/src/lib/providers/providers.tsx
Normal file
@@ -0,0 +1,34 @@
|
||||
import { NuqsAdapter } from "nuqs/adapters/next/app";
|
||||
import { memo } from "react";
|
||||
|
||||
import { I18nProvider } from "@turbostarter/i18n";
|
||||
|
||||
import { appConfig } from "~/config/app";
|
||||
import { QueryClientProvider } from "~/lib/query/client";
|
||||
|
||||
import { AnalyticsProvider } from "./analytics";
|
||||
import { MonitoringProvider } from "./monitoring";
|
||||
import { ThemeProvider } from "./theme";
|
||||
|
||||
interface ProvidersProps {
|
||||
readonly children: React.ReactNode;
|
||||
readonly locale: string;
|
||||
}
|
||||
|
||||
export const Providers = memo<ProvidersProps>(({ children, locale }) => {
|
||||
return (
|
||||
<I18nProvider locale={locale} defaultLocale={appConfig.locale}>
|
||||
<QueryClientProvider>
|
||||
<NuqsAdapter>
|
||||
<AnalyticsProvider>
|
||||
<MonitoringProvider>
|
||||
<ThemeProvider>{children}</ThemeProvider>
|
||||
</MonitoringProvider>
|
||||
</AnalyticsProvider>
|
||||
</NuqsAdapter>
|
||||
</QueryClientProvider>
|
||||
</I18nProvider>
|
||||
);
|
||||
});
|
||||
|
||||
Providers.displayName = "Providers";
|
||||
50
apps/web/src/lib/providers/theme.tsx
Normal file
50
apps/web/src/lib/providers/theme.tsx
Normal file
@@ -0,0 +1,50 @@
|
||||
"use client";
|
||||
|
||||
import { ThemeProvider as NextThemeProvider } from "next-themes";
|
||||
import { memo, useEffect } from "react";
|
||||
import { create } from "zustand";
|
||||
import { persist } from "zustand/middleware";
|
||||
|
||||
import { appConfig } from "~/config/app";
|
||||
|
||||
import type { ThemeConfig } from "@turbostarter/ui";
|
||||
|
||||
export const useThemeConfig = create<{
|
||||
config: Omit<ThemeConfig, "mode">;
|
||||
setConfig: (config: Omit<ThemeConfig, "mode">) => void;
|
||||
}>()(
|
||||
persist(
|
||||
(set) => ({
|
||||
config: appConfig.theme,
|
||||
setConfig: (config) => set({ config }),
|
||||
}),
|
||||
{
|
||||
name: "theme-config",
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
interface ThemeProviderProps {
|
||||
readonly children: React.ReactNode;
|
||||
}
|
||||
|
||||
export const ThemeProvider = memo<ThemeProviderProps>(({ children }) => {
|
||||
const config = useThemeConfig((s) => s.config);
|
||||
|
||||
useEffect(() => {
|
||||
document.body.dataset.theme = config.color;
|
||||
}, [config.color]);
|
||||
|
||||
return (
|
||||
<NextThemeProvider
|
||||
attribute="class"
|
||||
defaultTheme={appConfig.theme.mode}
|
||||
enableSystem
|
||||
disableTransitionOnChange
|
||||
>
|
||||
{children}
|
||||
</NextThemeProvider>
|
||||
);
|
||||
});
|
||||
|
||||
ThemeProvider.displayName = "ThemeProvider";
|
||||
Reference in New Issue
Block a user