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:
Alejandro Gutiérrez
2026-04-04 21:19:32 +01:00
commit d3163a5bff
1384 changed files with 314925 additions and 0 deletions

View 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>;
};

View 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}</>;
};

View 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";

View 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";