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 @@
export * from "./providers/env";

View File

@@ -0,0 +1 @@
export * from "./providers";

View File

@@ -0,0 +1 @@
export * from "./posthog/env";

View File

@@ -0,0 +1 @@
export * from "./posthog";

View File

@@ -0,0 +1,29 @@
/* eslint-disable turbo/no-undeclared-env-vars */
import { defineEnv } from "envin";
import * as z from "zod";
import { envConfig } from "@turbostarter/shared/constants";
import type { Preset } from "envin/types";
export const preset = {
id: "posthog",
clientPrefix: "NEXT_PUBLIC_",
client: {
NEXT_PUBLIC_POSTHOG_KEY: z.string().optional(),
NEXT_PUBLIC_POSTHOG_HOST: z
.string()
.optional()
.default("https://us.i.posthog.com"),
},
} as const satisfies Preset;
export const env = defineEnv({
...envConfig,
...preset,
env: {
...process.env,
NEXT_PUBLIC_POSTHOG_KEY: process.env.NEXT_PUBLIC_POSTHOG_KEY,
NEXT_PUBLIC_POSTHOG_HOST: process.env.NEXT_PUBLIC_POSTHOG_HOST,
},
});

View File

@@ -0,0 +1,44 @@
import posthog from "posthog-js";
import { env } from "./env";
import type { MonitoringProviderClientStrategy } from "../types";
const isValidPosthogConfig =
env.NEXT_PUBLIC_POSTHOG_KEY &&
env.NEXT_PUBLIC_POSTHOG_KEY !== "notyet" &&
env.NEXT_PUBLIC_POSTHOG_HOST.startsWith("http");
let initialized = false;
export const {
captureException,
identify,
initialize,
onRouterTransitionStart,
} = {
captureException: (exception) => {
if (!initialized) return;
posthog.captureException(exception);
},
identify: <T extends { id: string }>(user: T | null) => {
if (!initialized) return;
if (user) {
posthog.identify(user.id);
} else {
posthog.reset();
}
},
initialize: () => {
if (!isValidPosthogConfig) return;
const key = env.NEXT_PUBLIC_POSTHOG_KEY;
if (!key) return;
posthog.init(key, {
api_host: env.NEXT_PUBLIC_POSTHOG_HOST,
});
initialized = true;
},
onRouterTransitionStart: () => {
/* PostHog does not provide a way to capture router transitions yet */
},
} satisfies MonitoringProviderClientStrategy;

View File

@@ -0,0 +1,81 @@
import { PostHog } from "posthog-node";
import { env } from "./env";
import type { MonitoringProviderServerStrategy } from "../types";
const isValidPosthogConfig =
env.NEXT_PUBLIC_POSTHOG_KEY &&
env.NEXT_PUBLIC_POSTHOG_KEY !== "notyet" &&
env.NEXT_PUBLIC_POSTHOG_HOST.startsWith("http");
let posthogInstance: PostHog | null = null;
export function getPostHogServer() {
if (!isValidPosthogConfig) {
return null;
}
const key = env.NEXT_PUBLIC_POSTHOG_KEY;
if (!key) return null;
posthogInstance ??= new PostHog(key, {
host: env.NEXT_PUBLIC_POSTHOG_HOST,
flushAt: 1,
flushInterval: 0,
});
return posthogInstance;
}
export const { captureException, initialize, onRequestError } = {
captureException: (exception, extra = undefined) => {
const posthog = getPostHogServer();
if (!posthog) return;
const distinctId = typeof extra?.id === "string" ? extra.id : undefined;
posthog.captureException(exception, distinctId, extra);
},
initialize: () => {
getPostHogServer();
},
onRequestError: (error, request) => {
/* eslint-disable-next-line turbo/no-undeclared-env-vars, no-restricted-properties */
if (process.env.NEXT_RUNTIME !== "nodejs") {
return;
}
const posthog = getPostHogServer();
if (!posthog) return;
let distinctId: string | undefined;
if (request.headers.cookie) {
const cookieString = Array.isArray(request.headers.cookie)
? request.headers.cookie.join("; ")
: request.headers.cookie;
const postHogCookieMatch = /ph_phc_.*?_posthog=([^;]+)/.exec(
cookieString,
);
if (postHogCookieMatch?.[1]) {
try {
const decodedCookie = decodeURIComponent(postHogCookieMatch[1]);
const data: unknown = JSON.parse(decodedCookie);
if (
typeof data === "object" &&
data !== null &&
"distinct_id" in data &&
typeof data.distinct_id === "string"
) {
distinctId = data.distinct_id;
}
} catch {
/* Ignore */
}
}
}
posthog.captureException(error, distinctId);
},
} satisfies MonitoringProviderServerStrategy;

View File

@@ -0,0 +1,26 @@
/* eslint-disable turbo/no-undeclared-env-vars */
import { defineEnv } from "envin";
import * as z from "zod";
import { envConfig } from "@turbostarter/shared/constants";
import type { Preset } from "envin/types";
export const preset = {
id: "sentry",
clientPrefix: "NEXT_PUBLIC_",
client: {
NEXT_PUBLIC_SENTRY_DSN: z.string().optional().default(""),
NEXT_PUBLIC_SENTRY_ENVIRONMENT: z.string().default(process.env.NODE_ENV),
},
} as const satisfies Preset;
export const env = defineEnv({
...envConfig,
...preset,
env: {
...process.env,
NEXT_PUBLIC_SENTRY_DSN: process.env.NEXT_PUBLIC_SENTRY_DSN,
NEXT_PUBLIC_SENTRY_ENVIRONMENT: process.env.NEXT_PUBLIC_SENTRY_ENVIRONMENT,
},
});

View File

@@ -0,0 +1,51 @@
import * as Sentry from "@sentry/nextjs";
import { env } from "./env";
import type { MonitoringProviderClientStrategy } from "../types";
export const {
captureException,
identify,
initialize,
onRouterTransitionStart,
} = {
captureException: (exception) => {
Sentry.captureException(exception);
},
identify: (user: Sentry.User | null) => {
Sentry.setUser(user);
},
initialize: () => {
const environment = env.NEXT_PUBLIC_SENTRY_ENVIRONMENT;
Sentry.init({
dsn: env.NEXT_PUBLIC_SENTRY_DSN,
environment,
// Replay may only be enabled for the client-side
integrations: [
// add your desired integrations here
// https://docs.sentry.io/platforms/javascript/configuration/integrations/
],
// Set tracesSampleRate to 1.0 to capture 100%
// of transactions for performance monitoring.
// We recommend adjusting this value in production
tracesSampleRate: 1.0,
// Capture Replay for 10% of all sessions,
// plus for 100% of sessions with an error
replaysSessionSampleRate: 0.1,
replaysOnErrorSampleRate: 1.0,
// Adds more context data to events (IP address, cookies, user, etc.)
// For more information, visit: https://docs.sentry.io/platforms/react-native/data-management/data-collected/
sendDefaultPii: true,
// Note: if you want to override the automatic release value, do not set a
// `release` value here - use the environment variable `SENTRY_RELEASE`, so
// that it will also get attached to your source maps,
});
},
onRouterTransitionStart: Sentry.captureRouterTransitionStart,
} satisfies MonitoringProviderClientStrategy;

View File

@@ -0,0 +1,28 @@
import * as Sentry from "@sentry/nextjs";
import { env } from "./env";
import type { MonitoringProviderServerStrategy } from "../types";
export const { captureException, initialize, onRequestError } = {
captureException: (exception) => {
Sentry.captureException(exception);
},
initialize: () => {
const environment = env.NEXT_PUBLIC_SENTRY_ENVIRONMENT;
Sentry.init({
dsn: env.NEXT_PUBLIC_SENTRY_DSN,
environment,
// Adds more context data to events (IP address, cookies, user, etc.)
// For more information, visit: https://docs.sentry.io/platforms/react-native/data-management/data-collected/
sendDefaultPii: true,
// Note: if you want to override the automatic release value, do not set a
// `release` value here - use the environment variable `SENTRY_RELEASE`, so
// that it will also get attached to your source maps,
});
},
onRequestError: Sentry.captureRequestError,
} satisfies MonitoringProviderServerStrategy;

View File

@@ -0,0 +1 @@
export * from "./posthog/server";

View File

@@ -0,0 +1,26 @@
import type { MonitoringProviderStrategy } from "@turbostarter/monitoring";
export interface MonitoringProviderClientStrategy
extends MonitoringProviderStrategy {
onRouterTransitionStart: (
href: string,
navigationType: string,
) => void | Promise<void>;
}
export interface MonitoringProviderServerStrategy
extends Omit<MonitoringProviderStrategy, "identify"> {
onRequestError: (
error: unknown,
errorRequest: Readonly<{
path: string;
method: string;
headers: NodeJS.Dict<string | string[]>;
}>,
errorContext: Readonly<{
routerKind: string;
routePath: string;
routeType: string;
}>,
) => void | Promise<void>;
}

View File

@@ -0,0 +1 @@
export * from "./providers/server";