feat(ai): update AI settings to use organization-based access control

- Refactor AI-related schemas and services to use organizationId instead of adminId
- Update AI router to check organization-level permissions
- Modify AI settings creation and retrieval to work with organization context
- Adjust server-side props and access checks for AI settings
This commit is contained in:
Mauricio Siu
2025-03-02 00:54:46 -06:00
parent 747c2137c9
commit f78cda9cce
10 changed files with 5326 additions and 64 deletions

View File

@@ -0,0 +1,12 @@
CREATE TABLE "ai" (
"aiId" text PRIMARY KEY NOT NULL,
"name" text NOT NULL,
"apiUrl" text NOT NULL,
"apiKey" text NOT NULL,
"model" text NOT NULL,
"isEnabled" boolean DEFAULT true NOT NULL,
"organizationId" text NOT NULL,
"createdAt" text NOT NULL
);
--> statement-breakpoint
ALTER TABLE "ai" ADD CONSTRAINT "ai_organizationId_organization_id_fk" FOREIGN KEY ("organizationId") REFERENCES "public"."organization"("id") ON DELETE cascade ON UPDATE no action;

File diff suppressed because it is too large Load Diff

View File

@@ -477,6 +477,13 @@
"when": 1740892043121, "when": 1740892043121,
"tag": "0067_condemned_sugar_man", "tag": "0067_condemned_sugar_man",
"breakpoints": true "breakpoints": true
},
{
"idx": 68,
"version": "7",
"when": 1740897756774,
"tag": "0068_complex_rhino",
"breakpoints": true
} }
] ]
} }

View File

@@ -143,7 +143,8 @@
"xterm-addon-fit": "^0.8.0", "xterm-addon-fit": "^0.8.0",
"zod": "^3.23.4", "zod": "^3.23.4",
"zod-form-data": "^2.0.2", "zod-form-data": "^2.0.2",
"@faker-js/faker": "^8.4.1" "@faker-js/faker": "^8.4.1",
"@tailwindcss/typography": "0.5.16"
}, },
"devDependencies": { "devDependencies": {
"@types/qrcode": "^1.5.5", "@types/qrcode": "^1.5.5",

View File

@@ -25,7 +25,7 @@ export async function getServerSideProps(
ctx: GetServerSidePropsContext<{ serviceId: string }>, ctx: GetServerSidePropsContext<{ serviceId: string }>,
) { ) {
const { req, res } = ctx; const { req, res } = ctx;
const { user, session } = await validateRequest(req, res); const { user, session } = await validateRequest(req);
const locale = getLocale(req.cookies); const locale = getLocale(req.cookies);
const helpers = createServerSideHelpers({ const helpers = createServerSideHelpers({
@@ -34,22 +34,17 @@ export async function getServerSideProps(
req: req as any, req: req as any,
res: res as any, res: res as any,
db: null as any, db: null as any,
session: session, session: session as any,
user: user, user: user as any,
}, },
transformer: superjson, transformer: superjson,
}); });
await helpers.settings.isCloud.prefetch(); await helpers.settings.isCloud.prefetch();
await helpers.auth.get.prefetch(); await helpers.user.get.prefetch();
if (user?.rol === "user") {
await helpers.user.byAuthId.prefetch({
authId: user.authId,
});
}
if (!user) { if (!user || user.role === "member") {
return { return {
redirect: { redirect: {
permanent: true, permanent: true,

View File

@@ -15,7 +15,7 @@ import { createDomain } from "@dokploy/server/index";
import { import {
deleteAiSettings, deleteAiSettings,
getAiSettingById, getAiSettingById,
getAiSettingsByAdminId, getAiSettingsByOrganizationId,
saveAiSettings, saveAiSettings,
suggestVariants, suggestVariants,
} from "@dokploy/server/services/ai"; } from "@dokploy/server/services/ai";
@@ -33,7 +33,7 @@ export const aiRouter = createTRPCRouter({
.input(z.object({ aiId: z.string() })) .input(z.object({ aiId: z.string() }))
.query(async ({ ctx, input }) => { .query(async ({ ctx, input }) => {
const aiSetting = await getAiSettingById(input.aiId); const aiSetting = await getAiSettingById(input.aiId);
if (aiSetting.adminId !== ctx.user.adminId) { if (aiSetting.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({ throw new TRPCError({
code: "UNAUTHORIZED", code: "UNAUTHORIZED",
message: "You don't have access to this AI configuration", message: "You don't have access to this AI configuration",
@@ -42,24 +42,26 @@ export const aiRouter = createTRPCRouter({
return aiSetting; return aiSetting;
}), }),
create: adminProcedure.input(apiCreateAi).mutation(async ({ ctx, input }) => { create: adminProcedure.input(apiCreateAi).mutation(async ({ ctx, input }) => {
return await saveAiSettings(ctx.user.adminId, input); return await saveAiSettings(ctx.session.activeOrganizationId, input);
}), }),
update: protectedProcedure update: protectedProcedure
.input(apiUpdateAi) .input(apiUpdateAi)
.mutation(async ({ ctx, input }) => { .mutation(async ({ ctx, input }) => {
return await saveAiSettings(ctx.user.adminId, input); return await saveAiSettings(ctx.session.activeOrganizationId, input);
}), }),
getAll: adminProcedure.query(async ({ ctx }) => { getAll: adminProcedure.query(async ({ ctx }) => {
return await getAiSettingsByAdminId(ctx.user.adminId); return await getAiSettingsByOrganizationId(
ctx.session.activeOrganizationId,
);
}), }),
get: protectedProcedure get: protectedProcedure
.input(z.object({ aiId: z.string() })) .input(z.object({ aiId: z.string() }))
.query(async ({ ctx, input }) => { .query(async ({ ctx, input }) => {
const aiSetting = await getAiSettingById(input.aiId); const aiSetting = await getAiSettingById(input.aiId);
if (aiSetting.adminId !== ctx.user.authId) { if (aiSetting.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({ throw new TRPCError({
code: "UNAUTHORIZED", code: "UNAUTHORIZED",
message: "You don't have access to this AI configuration", message: "You don't have access to this AI configuration",
@@ -72,7 +74,7 @@ export const aiRouter = createTRPCRouter({
.input(z.object({ aiId: z.string() })) .input(z.object({ aiId: z.string() }))
.mutation(async ({ ctx, input }) => { .mutation(async ({ ctx, input }) => {
const aiSetting = await getAiSettingById(input.aiId); const aiSetting = await getAiSettingById(input.aiId);
if (aiSetting.adminId !== ctx.user.adminId) { if (aiSetting.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({ throw new TRPCError({
code: "UNAUTHORIZED", code: "UNAUTHORIZED",
message: "You don't have access to this AI configuration", message: "You don't have access to this AI configuration",
@@ -93,7 +95,7 @@ export const aiRouter = createTRPCRouter({
try { try {
return await suggestVariants({ return await suggestVariants({
...input, ...input,
adminId: ctx.user.adminId, organizationId: ctx.session.activeOrganizationId,
}); });
} catch (error) { } catch (error) {
throw new TRPCError({ throw new TRPCError({
@@ -105,8 +107,12 @@ export const aiRouter = createTRPCRouter({
deploy: protectedProcedure deploy: protectedProcedure
.input(deploySuggestionSchema) .input(deploySuggestionSchema)
.mutation(async ({ ctx, input }) => { .mutation(async ({ ctx, input }) => {
if (ctx.user.rol === "user") { if (ctx.user.rol === "member") {
await checkServiceAccess(ctx.user.adminId, input.projectId, "create"); await checkServiceAccess(
ctx.session.activeOrganizationId,
input.projectId,
"create",
);
} }
if (IS_CLOUD && !input.serverId) { if (IS_CLOUD && !input.serverId) {
@@ -143,8 +149,12 @@ export const aiRouter = createTRPCRouter({
} }
} }
if (ctx.user.rol === "user") { if (ctx.user.rol === "member") {
await addNewService(ctx.user.authId, compose.composeId); await addNewService(
ctx.session.activeOrganizationId,
ctx.user.ownerId,
compose.composeId,
);
} }
return null; return null;

View File

@@ -3,8 +3,7 @@ import { boolean, pgTable, text } from "drizzle-orm/pg-core";
import { createInsertSchema } from "drizzle-zod"; import { createInsertSchema } from "drizzle-zod";
import { nanoid } from "nanoid"; import { nanoid } from "nanoid";
import { z } from "zod"; import { z } from "zod";
import { admins } from "./admin"; import { organization } from "./account";
export const ai = pgTable("ai", { export const ai = pgTable("ai", {
aiId: text("aiId") aiId: text("aiId")
.notNull() .notNull()
@@ -15,18 +14,18 @@ export const ai = pgTable("ai", {
apiKey: text("apiKey").notNull(), apiKey: text("apiKey").notNull(),
model: text("model").notNull(), model: text("model").notNull(),
isEnabled: boolean("isEnabled").notNull().default(true), isEnabled: boolean("isEnabled").notNull().default(true),
adminId: text("adminId") organizationId: text("organizationId")
.notNull() .notNull()
.references(() => admins.adminId, { onDelete: "cascade" }), // Admin ID who created the AI settings .references(() => organization.id, { onDelete: "cascade" }), // Admin ID who created the AI settings
createdAt: text("createdAt") createdAt: text("createdAt")
.notNull() .notNull()
.$defaultFn(() => new Date().toISOString()), .$defaultFn(() => new Date().toISOString()),
}); });
export const aiRelations = relations(ai, ({ one }) => ({ export const aiRelations = relations(ai, ({ one }) => ({
admin: one(admins, { organization: one(organization, {
fields: [ai.adminId], fields: [ai.organizationId],
references: [admins.adminId], references: [organization.id],
}), }),
})); }));
@@ -53,7 +52,7 @@ export const apiUpdateAi = createSchema
.extend({ .extend({
aiId: z.string().min(1), aiId: z.string().min(1),
}) })
.omit({ adminId: true }); .omit({ organizationId: true });
export const deploySuggestionSchema = z.object({ export const deploySuggestionSchema = z.object({
projectId: z.string().min(1), projectId: z.string().min(1),

View File

@@ -28,6 +28,9 @@ export const findUserById = async (userId: string) => {
export const findOrganizationById = async (organizationId: string) => { export const findOrganizationById = async (organizationId: string) => {
const organizationResult = await db.query.organization.findFirst({ const organizationResult = await db.query.organization.findFirst({
where: eq(organization.id, organizationId), where: eq(organization.id, organizationId),
with: {
owner: true,
},
}); });
return organizationResult; return organizationResult;
}; };

View File

@@ -6,12 +6,12 @@ import { generateObject } from "ai";
import { desc, eq } from "drizzle-orm"; import { desc, eq } from "drizzle-orm";
import { z } from "zod"; import { z } from "zod";
import { IS_CLOUD } from "../constants"; import { IS_CLOUD } from "../constants";
import { findAdminById } from "./admin";
import { findServerById } from "./server"; import { findServerById } from "./server";
import { findOrganizationById } from "./admin";
export const getAiSettingsByAdminId = async (adminId: string) => { export const getAiSettingsByOrganizationId = async (organizationId: string) => {
const aiSettings = await db.query.ai.findMany({ const aiSettings = await db.query.ai.findMany({
where: eq(ai.adminId, adminId), where: eq(ai.organizationId, organizationId),
orderBy: desc(ai.createdAt), orderBy: desc(ai.createdAt),
}); });
return aiSettings; return aiSettings;
@@ -30,14 +30,14 @@ export const getAiSettingById = async (aiId: string) => {
return aiSetting; return aiSetting;
}; };
export const saveAiSettings = async (adminId: string, settings: any) => { export const saveAiSettings = async (organizationId: string, settings: any) => {
const aiId = settings.aiId; const aiId = settings.aiId;
return db return db
.insert(ai) .insert(ai)
.values({ .values({
aiId, aiId,
adminId, organizationId,
...settings, ...settings,
}) })
.onConflictDoUpdate({ .onConflictDoUpdate({
@@ -53,14 +53,14 @@ export const deleteAiSettings = async (aiId: string) => {
}; };
interface Props { interface Props {
adminId: string; organizationId: string;
aiId: string; aiId: string;
input: string; input: string;
serverId?: string | undefined; serverId?: string | undefined;
} }
export const suggestVariants = async ({ export const suggestVariants = async ({
adminId, organizationId,
aiId, aiId,
input, input,
serverId, serverId,
@@ -79,8 +79,8 @@ export const suggestVariants = async ({
let ip = ""; let ip = "";
if (!IS_CLOUD) { if (!IS_CLOUD) {
const admin = await findAdminById(adminId); const organization = await findOrganizationById(organizationId);
ip = admin?.serverIp || ""; ip = organization?.owner.serverIp || "";
} }
if (serverId) { if (serverId) {

165
pnpm-lock.yaml generated
View File

@@ -202,6 +202,9 @@ importers:
'@stripe/stripe-js': '@stripe/stripe-js':
specifier: 4.8.0 specifier: 4.8.0
version: 4.8.0 version: 4.8.0
'@tailwindcss/typography':
specifier: 0.5.16
version: 0.5.16(tailwindcss@3.4.7(ts-node@10.9.2(@types/node@18.19.42)(typescript@5.5.3)))
'@tanstack/react-query': '@tanstack/react-query':
specifier: ^4.36.1 specifier: ^4.36.1
version: 4.36.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0) version: 4.36.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
@@ -282,10 +285,10 @@ importers:
version: 16.4.5 version: 16.4.5
drizzle-orm: drizzle-orm:
specifier: ^0.39.1 specifier: ^0.39.1
version: 0.39.1(@types/react@18.3.5)(kysely@0.27.5)(postgres@3.4.4)(react@18.2.0)(sqlite3@5.1.7) version: 0.39.1(@opentelemetry/api@1.9.0)(@types/react@18.3.5)(kysely@0.27.5)(postgres@3.4.4)(react@18.2.0)(sqlite3@5.1.7)
drizzle-zod: drizzle-zod:
specifier: 0.5.1 specifier: 0.5.1
version: 0.5.1(drizzle-orm@0.39.1(@types/react@18.3.5)(kysely@0.27.5)(postgres@3.4.4)(react@18.2.0)(sqlite3@5.1.7))(zod@3.23.8) version: 0.5.1(drizzle-orm@0.39.1(@opentelemetry/api@1.9.0)(@types/react@18.3.5)(kysely@0.27.5)(postgres@3.4.4)(react@18.2.0)(sqlite3@5.1.7))(zod@3.23.8)
fancy-ansi: fancy-ansi:
specifier: ^0.1.3 specifier: ^0.1.3
version: 0.1.3 version: 0.1.3
@@ -324,7 +327,7 @@ importers:
version: 15.3.1(i18next@23.16.5)(next@15.0.1(@opentelemetry/api@1.9.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(react-i18next@15.1.1(i18next@23.16.5)(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(react@18.2.0) version: 15.3.1(i18next@23.16.5)(next@15.0.1(@opentelemetry/api@1.9.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(react-i18next@15.1.1(i18next@23.16.5)(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(react@18.2.0)
next-themes: next-themes:
specifier: ^0.2.1 specifier: ^0.2.1
version: 0.2.1(next@15.0.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(react-dom@18.2.0(react@18.2.0))(react@18.2.0) version: 0.2.1(next@15.0.1(@opentelemetry/api@1.9.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
node-os-utils: node-os-utils:
specifier: 1.3.7 specifier: 1.3.7
version: 1.3.7 version: 1.3.7
@@ -422,9 +425,6 @@ importers:
specifier: ^2.0.2 specifier: ^2.0.2
version: 2.0.2(zod@3.23.8) version: 2.0.2(zod@3.23.8)
devDependencies: devDependencies:
'@tailwindcss/typography':
specifier: ^0.5.15
version: 0.5.16(tailwindcss@3.4.7)
'@types/adm-zip': '@types/adm-zip':
specifier: ^0.5.5 specifier: ^0.5.5
version: 0.5.5 version: 0.5.5
@@ -520,7 +520,7 @@ importers:
version: 16.4.5 version: 16.4.5
drizzle-orm: drizzle-orm:
specifier: ^0.39.1 specifier: ^0.39.1
version: 0.39.1(@types/react@18.3.5)(kysely@0.27.5)(postgres@3.4.4)(react@18.2.0)(sqlite3@5.1.7) version: 0.39.1(@opentelemetry/api@1.9.0)(@types/react@18.3.5)(kysely@0.27.5)(postgres@3.4.4)(react@18.2.0)(sqlite3@5.1.7)
hono: hono:
specifier: ^4.5.8 specifier: ^4.5.8
version: 4.5.8 version: 4.5.8
@@ -561,6 +561,27 @@ importers:
packages/server: packages/server:
dependencies: dependencies:
'@ai-sdk/anthropic':
specifier: ^1.0.6
version: 1.0.8(zod@3.23.8)
'@ai-sdk/azure':
specifier: ^1.0.15
version: 1.0.18(zod@3.23.8)
'@ai-sdk/cohere':
specifier: ^1.0.6
version: 1.0.8(zod@3.23.8)
'@ai-sdk/deepinfra':
specifier: ^0.0.4
version: 0.0.4(zod@3.23.8)
'@ai-sdk/mistral':
specifier: ^1.0.6
version: 1.0.8(zod@3.23.8)
'@ai-sdk/openai':
specifier: ^1.0.12
version: 1.0.16(zod@3.23.8)
'@ai-sdk/openai-compatible':
specifier: ^0.0.13
version: 0.0.13(zod@3.23.8)
'@better-auth/utils': '@better-auth/utils':
specifier: 0.2.3 specifier: 0.2.3
version: 0.2.3 version: 0.2.3
@@ -614,13 +635,13 @@ importers:
version: 16.4.5 version: 16.4.5
drizzle-dbml-generator: drizzle-dbml-generator:
specifier: 0.10.0 specifier: 0.10.0
version: 0.10.0(drizzle-orm@0.39.1(@types/react@18.3.5)(kysely@0.27.5)(postgres@3.4.4)(react@18.2.0)(sqlite3@5.1.7)) version: 0.10.0(drizzle-orm@0.39.1(@opentelemetry/api@1.9.0)(@types/react@18.3.5)(kysely@0.27.5)(postgres@3.4.4)(react@18.2.0)(sqlite3@5.1.7))
drizzle-orm: drizzle-orm:
specifier: ^0.39.1 specifier: ^0.39.1
version: 0.39.1(@types/react@18.3.5)(kysely@0.27.5)(postgres@3.4.4)(react@18.2.0)(sqlite3@5.1.7) version: 0.39.1(@opentelemetry/api@1.9.0)(@types/react@18.3.5)(kysely@0.27.5)(postgres@3.4.4)(react@18.2.0)(sqlite3@5.1.7)
drizzle-zod: drizzle-zod:
specifier: 0.5.1 specifier: 0.5.1
version: 0.5.1(drizzle-orm@0.39.1(@types/react@18.3.5)(kysely@0.27.5)(postgres@3.4.4)(react@18.2.0)(sqlite3@5.1.7))(zod@3.23.8) version: 0.5.1(drizzle-orm@0.39.1(@opentelemetry/api@1.9.0)(@types/react@18.3.5)(kysely@0.27.5)(postgres@3.4.4)(react@18.2.0)(sqlite3@5.1.7))(zod@3.23.8)
hi-base32: hi-base32:
specifier: ^0.5.1 specifier: ^0.5.1
version: 0.5.1 version: 0.5.1
@@ -2280,6 +2301,10 @@ packages:
'@one-ini/wasm@0.1.1': '@one-ini/wasm@0.1.1':
resolution: {integrity: sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==} resolution: {integrity: sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==}
'@opentelemetry/api@1.9.0':
resolution: {integrity: sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==}
engines: {node: '>=8.0.0'}
'@oslojs/asn1@1.0.0': '@oslojs/asn1@1.0.0':
resolution: {integrity: sha512-zw/wn0sj0j0QKbIXfIlnEcTviaCzYOY3V5rAyjR6YtOByFtJiT574+8p9Wlach0lZH9fddD4yb9laEAIl4vXQA==} resolution: {integrity: sha512-zw/wn0sj0j0QKbIXfIlnEcTviaCzYOY3V5rAyjR6YtOByFtJiT574+8p9Wlach0lZH9fddD4yb9laEAIl4vXQA==}
@@ -4912,6 +4937,9 @@ packages:
resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==} resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==}
engines: {node: '>=6'} engines: {node: '>=6'}
extend@3.0.2:
resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==}
fancy-ansi@0.1.3: fancy-ansi@0.1.3:
resolution: {integrity: sha512-tRQVTo5jjdSIiydqgzIIEZpKddzSsfGLsSVt6vWdjVm7fbvDTiQkyoPu6Z3dIPlAM4OZk0jP5jmTCX4G8WGgBw==} resolution: {integrity: sha512-tRQVTo5jjdSIiydqgzIIEZpKddzSsfGLsSVt6vWdjVm7fbvDTiQkyoPu6Z3dIPlAM4OZk0jP5jmTCX4G8WGgBw==}
@@ -5393,6 +5421,10 @@ packages:
resolution: {integrity: sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==} resolution: {integrity: sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==}
engines: {node: '>=8'} engines: {node: '>=8'}
is-plain-obj@4.1.0:
resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==}
engines: {node: '>=12'}
is-stream@3.0.0: is-stream@3.0.0:
resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==} resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
@@ -6015,6 +6047,11 @@ packages:
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
hasBin: true hasBin: true
nanoid@3.3.8:
resolution: {integrity: sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==}
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
hasBin: true
nanostores@0.11.3: nanostores@0.11.3:
resolution: {integrity: sha512-TUes3xKIX33re4QzdxwZ6tdbodjmn3tWXCEc1uokiEmo14sI1EaGYNs2k3bU2pyyGNmBqFGAVl6jAGWd06AVIg==} resolution: {integrity: sha512-TUes3xKIX33re4QzdxwZ6tdbodjmn3tWXCEc1uokiEmo14sI1EaGYNs2k3bU2pyyGNmBqFGAVl6jAGWd06AVIg==}
engines: {node: ^18.0.0 || >=20.0.0} engines: {node: ^18.0.0 || >=20.0.0}
@@ -7417,12 +7454,30 @@ packages:
resolution: {integrity: sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==} resolution: {integrity: sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==}
engines: {node: '>=18'} engines: {node: '>=18'}
unified@11.0.5:
resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==}
unique-filename@1.1.1: unique-filename@1.1.1:
resolution: {integrity: sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==} resolution: {integrity: sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==}
unique-slug@2.0.2: unique-slug@2.0.2:
resolution: {integrity: sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==} resolution: {integrity: sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==}
unist-util-is@6.0.0:
resolution: {integrity: sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==}
unist-util-position@5.0.0:
resolution: {integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==}
unist-util-stringify-position@4.0.0:
resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==}
unist-util-visit-parents@6.0.1:
resolution: {integrity: sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==}
unist-util-visit@5.0.0:
resolution: {integrity: sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==}
universal-github-app-jwt@1.1.2: universal-github-app-jwt@1.1.2:
resolution: {integrity: sha512-t1iB2FmLFE+yyJY9+3wMx0ejB+MQpEVkH0gQv7dR6FZyltyq+ZZO0uDpbopxhrZ3SLEO4dCEkIujOMldEQ2iOA==} resolution: {integrity: sha512-t1iB2FmLFE+yyJY9+3wMx0ejB+MQpEVkH0gQv7dR6FZyltyq+ZZO0uDpbopxhrZ3SLEO4dCEkIujOMldEQ2iOA==}
@@ -7502,6 +7557,12 @@ packages:
typescript: typescript:
optional: true optional: true
vfile-message@4.0.2:
resolution: {integrity: sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==}
vfile@6.0.3:
resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==}
victory-vendor@36.9.2: victory-vendor@36.9.2:
resolution: {integrity: sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ==} resolution: {integrity: sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ==}
@@ -7743,6 +7804,9 @@ packages:
zod@3.24.1: zod@3.24.1:
resolution: {integrity: sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A==} resolution: {integrity: sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A==}
zwitch@2.0.4:
resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==}
snapshots: snapshots:
'@ai-sdk/anthropic@1.0.8(zod@3.23.8)': '@ai-sdk/anthropic@1.0.8(zod@3.23.8)':
@@ -8106,17 +8170,17 @@ snapshots:
'@emnapi/core@0.45.0': '@emnapi/core@0.45.0':
dependencies: dependencies:
tslib: 2.6.3 tslib: 2.8.1
optional: true optional: true
'@emnapi/runtime@0.45.0': '@emnapi/runtime@0.45.0':
dependencies: dependencies:
tslib: 2.6.3 tslib: 2.8.1
optional: true optional: true
'@emnapi/runtime@1.3.1': '@emnapi/runtime@1.3.1':
dependencies: dependencies:
tslib: 2.6.3 tslib: 2.8.1
optional: true optional: true
'@emotion/is-prop-valid@0.7.3': '@emotion/is-prop-valid@0.7.3':
@@ -9009,6 +9073,8 @@ snapshots:
'@one-ini/wasm@0.1.1': {} '@one-ini/wasm@0.1.1': {}
'@opentelemetry/api@1.9.0': {}
'@oslojs/asn1@1.0.0': '@oslojs/asn1@1.0.0':
dependencies: dependencies:
'@oslojs/binary': 1.0.0 '@oslojs/binary': 1.0.0
@@ -10409,19 +10475,19 @@ snapshots:
'@swc/helpers@0.5.13': '@swc/helpers@0.5.13':
dependencies: dependencies:
tslib: 2.6.3 tslib: 2.8.1
'@szmarczak/http-timer@5.0.1': '@szmarczak/http-timer@5.0.1':
dependencies: dependencies:
defer-to-connect: 2.0.1 defer-to-connect: 2.0.1
'@tailwindcss/typography@0.5.16(tailwindcss@3.4.7)': '@tailwindcss/typography@0.5.16(tailwindcss@3.4.7(ts-node@10.9.2(@types/node@18.19.42)(typescript@5.5.3)))':
dependencies: dependencies:
lodash.castarray: 4.4.0 lodash.castarray: 4.4.0
lodash.isplainobject: 4.0.6 lodash.isplainobject: 4.0.6
lodash.merge: 4.6.2 lodash.merge: 4.6.2
postcss-selector-parser: 6.0.10 postcss-selector-parser: 6.0.10
tailwindcss: 3.4.7 tailwindcss: 3.4.7(ts-node@10.9.2(@types/node@18.19.42)(typescript@5.5.3))
'@tanstack/query-core@4.36.1': {} '@tanstack/query-core@4.36.1': {}
@@ -10482,7 +10548,7 @@ snapshots:
'@tybys/wasm-util@0.8.3': '@tybys/wasm-util@0.8.3':
dependencies: dependencies:
tslib: 2.6.3 tslib: 2.8.1
optional: true optional: true
'@types/adm-zip@0.5.5': '@types/adm-zip@0.5.5':
@@ -11716,9 +11782,9 @@ snapshots:
drange@1.1.1: {} drange@1.1.1: {}
drizzle-dbml-generator@0.10.0(drizzle-orm@0.39.1(@types/react@18.3.5)(kysely@0.27.5)(postgres@3.4.4)(react@18.2.0)(sqlite3@5.1.7)): drizzle-dbml-generator@0.10.0(drizzle-orm@0.39.1(@opentelemetry/api@1.9.0)(@types/react@18.3.5)(kysely@0.27.5)(postgres@3.4.4)(react@18.2.0)(sqlite3@5.1.7)):
dependencies: dependencies:
drizzle-orm: 0.39.1(@types/react@18.3.5)(kysely@0.27.5)(postgres@3.4.4)(react@18.2.0)(sqlite3@5.1.7) drizzle-orm: 0.39.1(@opentelemetry/api@1.9.0)(@types/react@18.3.5)(kysely@0.27.5)(postgres@3.4.4)(react@18.2.0)(sqlite3@5.1.7)
drizzle-kit@0.30.4: drizzle-kit@0.30.4:
dependencies: dependencies:
@@ -11729,7 +11795,7 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
drizzle-orm@0.39.1(@types/react@18.3.5)(kysely@0.27.5)(postgres@3.4.4)(react@18.2.0)(sqlite3@5.1.7): drizzle-orm@0.39.1(@opentelemetry/api@1.9.0)(@types/react@18.3.5)(kysely@0.27.5)(postgres@3.4.4)(react@18.2.0)(sqlite3@5.1.7):
optionalDependencies: optionalDependencies:
'@opentelemetry/api': 1.9.0 '@opentelemetry/api': 1.9.0
'@types/react': 18.3.5 '@types/react': 18.3.5
@@ -11738,9 +11804,9 @@ snapshots:
react: 18.2.0 react: 18.2.0
sqlite3: 5.1.7 sqlite3: 5.1.7
drizzle-zod@0.5.1(drizzle-orm@0.39.1(@types/react@18.3.5)(kysely@0.27.5)(postgres@3.4.4)(react@18.2.0)(sqlite3@5.1.7))(zod@3.23.8): drizzle-zod@0.5.1(drizzle-orm@0.39.1(@opentelemetry/api@1.9.0)(@types/react@18.3.5)(kysely@0.27.5)(postgres@3.4.4)(react@18.2.0)(sqlite3@5.1.7))(zod@3.23.8):
dependencies: dependencies:
drizzle-orm: 0.39.1(@types/react@18.3.5)(kysely@0.27.5)(postgres@3.4.4)(react@18.2.0)(sqlite3@5.1.7) drizzle-orm: 0.39.1(@opentelemetry/api@1.9.0)(@types/react@18.3.5)(kysely@0.27.5)(postgres@3.4.4)(react@18.2.0)(sqlite3@5.1.7)
zod: 3.23.8 zod: 3.23.8
eastasianwidth@0.2.0: {} eastasianwidth@0.2.0: {}
@@ -11963,6 +12029,8 @@ snapshots:
expand-template@2.0.3: expand-template@2.0.3:
optional: true optional: true
extend@3.0.2: {}
fancy-ansi@0.1.3: fancy-ansi@0.1.3:
dependencies: dependencies:
escape-html: 1.0.3 escape-html: 1.0.3
@@ -12479,6 +12547,8 @@ snapshots:
is-obj@2.0.0: {} is-obj@2.0.0: {}
is-plain-obj@4.1.0: {}
is-stream@3.0.0: {} is-stream@3.0.0: {}
is-text-path@2.0.0: is-text-path@2.0.0:
@@ -12497,7 +12567,7 @@ snapshots:
jest-worker@27.5.1: jest-worker@27.5.1:
dependencies: dependencies:
'@types/node': 18.19.42 '@types/node': 20.14.10
merge-stream: 2.0.0 merge-stream: 2.0.0
supports-color: 8.1.1 supports-color: 8.1.1
@@ -13270,6 +13340,8 @@ snapshots:
nanoid@3.3.7: {} nanoid@3.3.7: {}
nanoid@3.3.8: {}
nanostores@0.11.3: {} nanostores@0.11.3: {}
napi-build-utils@1.0.2: napi-build-utils@1.0.2:
@@ -13299,7 +13371,7 @@ snapshots:
react: 18.2.0 react: 18.2.0
react-dom: 18.2.0(react@18.2.0) react-dom: 18.2.0(react@18.2.0)
next@15.0.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0): next@15.0.1(@opentelemetry/api@1.9.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0):
dependencies: dependencies:
'@next/env': 15.0.1 '@next/env': 15.0.1
'@swc/counter': 0.1.3 '@swc/counter': 0.1.3
@@ -14822,6 +14894,16 @@ snapshots:
unicorn-magic@0.1.0: {} unicorn-magic@0.1.0: {}
unified@11.0.5:
dependencies:
'@types/unist': 3.0.3
bail: 2.0.2
devlop: 1.1.0
extend: 3.0.2
is-plain-obj: 4.1.0
trough: 2.2.0
vfile: 6.0.3
unique-filename@1.1.1: unique-filename@1.1.1:
dependencies: dependencies:
unique-slug: 2.0.2 unique-slug: 2.0.2
@@ -14832,6 +14914,29 @@ snapshots:
imurmurhash: 0.1.4 imurmurhash: 0.1.4
optional: true optional: true
unist-util-is@6.0.0:
dependencies:
'@types/unist': 3.0.3
unist-util-position@5.0.0:
dependencies:
'@types/unist': 3.0.3
unist-util-stringify-position@4.0.0:
dependencies:
'@types/unist': 3.0.3
unist-util-visit-parents@6.0.1:
dependencies:
'@types/unist': 3.0.3
unist-util-is: 6.0.0
unist-util-visit@5.0.0:
dependencies:
'@types/unist': 3.0.3
unist-util-is: 6.0.0
unist-util-visit-parents: 6.0.1
universal-github-app-jwt@1.1.2: universal-github-app-jwt@1.1.2:
dependencies: dependencies:
'@types/jsonwebtoken': 9.0.6 '@types/jsonwebtoken': 9.0.6
@@ -14898,6 +15003,16 @@ snapshots:
optionalDependencies: optionalDependencies:
typescript: 5.5.3 typescript: 5.5.3
vfile-message@4.0.2:
dependencies:
'@types/unist': 3.0.3
unist-util-stringify-position: 4.0.0
vfile@6.0.3:
dependencies:
'@types/unist': 3.0.3
vfile-message: 4.0.2
victory-vendor@36.9.2: victory-vendor@36.9.2:
dependencies: dependencies:
'@types/d3-array': 3.2.1 '@types/d3-array': 3.2.1
@@ -15162,3 +15277,5 @@ snapshots:
zod@3.23.8: {} zod@3.23.8: {}
zod@3.24.1: {} zod@3.24.1: {}
zwitch@2.0.4: {}