diff --git a/src/actions/__tests__/syncCurrentUser.test.ts b/src/actions/__tests__/syncCurrentUser.test.ts index 07988ecd20..0800faa5e8 100644 --- a/src/actions/__tests__/syncCurrentUser.test.ts +++ b/src/actions/__tests__/syncCurrentUser.test.ts @@ -3,7 +3,7 @@ import { eq } from 'drizzle-orm'; import { logger } from '@/lib/server/logger'; -import { client as db, users, orgs } from '@/db'; +import { client as db, users, orgs } from '@/db/server'; import { syncCurrentUser } from '../sync'; diff --git a/src/actions/__tests__/syncOrg.test.ts b/src/actions/__tests__/syncOrg.test.ts index f06d6075ea..0ccb1b5b5b 100644 --- a/src/actions/__tests__/syncOrg.test.ts +++ b/src/actions/__tests__/syncOrg.test.ts @@ -4,7 +4,7 @@ import { eq } from 'drizzle-orm'; import { clerkClient } from '@clerk/nextjs/server'; import { logger } from '@/lib/server/logger'; -import { client as db, orgs } from '@/db'; +import { client as db, orgs } from '@/db/server'; import { syncOrg } from '../sync'; diff --git a/src/actions/analytics.ts b/src/actions/analytics.ts index 52489f148b..728fed9249 100644 --- a/src/actions/analytics.ts +++ b/src/actions/analytics.ts @@ -9,8 +9,7 @@ import { import type { TimePeriod } from '@/types'; import { analytics } from '@/lib/server'; -import * as db from '@/db'; -import { inArray } from 'drizzle-orm'; +import { type User, getUsersById } from '@/db/server'; /** * captureEvent @@ -106,7 +105,7 @@ const developerUsageSchema = z.object({ }); export type DeveloperUsage = z.infer & { - user: db.User; + user: User; }; export const getDeveloperUsage = async ({ @@ -140,25 +139,9 @@ export const getDeveloperUsage = async ({ .array(developerUsageSchema) .parse(await resultSet.json()); - const users = ( - await db.client - .select() - .from(db.users) - .where( - inArray( - db.users.id, - developerUsages.map(({ userId }) => userId), - ), - ) - ).reduce( - (acc, user) => ({ ...acc, [user.id]: user }), - {} as Record, - ); + const users = await getUsersById(developerUsages.map(({ userId }) => userId)); return developerUsages - .map((usage) => ({ - ...usage, - user: users[usage.userId], - })) + .map((usage) => ({ ...usage, user: users[usage.userId] })) .filter((usage): usage is DeveloperUsage => !!usage.user); }; diff --git a/src/actions/auditLogs.ts b/src/actions/auditLogs.ts index 3a9e309ed0..a2e7510472 100644 --- a/src/actions/auditLogs.ts +++ b/src/actions/auditLogs.ts @@ -9,7 +9,7 @@ import { type CreateAuditLog, client as db, auditLogs, -} from '@/db'; +} from '@/db/server'; export const getAuditLogs = async ({ orgId, diff --git a/src/actions/defaultParameters.ts b/src/actions/defaultParameters.ts index 348ed51724..79a5bf4ea1 100644 --- a/src/actions/defaultParameters.ts +++ b/src/actions/defaultParameters.ts @@ -4,7 +4,7 @@ import { sql } from 'drizzle-orm'; import { z } from 'zod'; import { type ApiResponse, ORGANIZATION_ALLOW_ALL } from '@/types'; -import { client as db, AuditLogTargetType, orgSettings } from '@/db'; +import { AuditLogTargetType, client as db, orgSettings } from '@/db/server'; import { isAuthSuccess, handleError } from '@/lib/server'; import { validateAuth } from './auth'; diff --git a/src/actions/organizationSettings.ts b/src/actions/organizationSettings.ts index e840927b44..880e656f4b 100644 --- a/src/actions/organizationSettings.ts +++ b/src/actions/organizationSettings.ts @@ -12,7 +12,7 @@ import { organizationAllowListSchema, organizationDefaultSettingsSchema, } from '@/types'; -import { client as db, AuditLogTargetType, orgSettings } from '@/db'; +import { AuditLogTargetType, client as db, orgSettings } from '@/db/server'; import { handleError, isAuthSuccess } from '@/lib/server'; import { validateAuth } from './auth'; diff --git a/src/actions/providerWhitelist.ts b/src/actions/providerWhitelist.ts index 64d0e731a9..c4ac0fa375 100644 --- a/src/actions/providerWhitelist.ts +++ b/src/actions/providerWhitelist.ts @@ -4,7 +4,7 @@ import { z } from 'zod'; import type { ApiResponse } from '@/types'; import { handleError, isAuthSuccess } from '@/lib/server'; -import { AuditLogTargetType } from '@/db'; +import { AuditLogTargetType } from '@/db/server'; import { validateAuth } from './auth'; import { createAuditLog } from './auditLogs'; diff --git a/src/actions/sync.ts b/src/actions/sync.ts index 2431fcb674..3dd69de393 100644 --- a/src/actions/sync.ts +++ b/src/actions/sync.ts @@ -9,7 +9,7 @@ import { client as db, users, orgs, -} from '@/db'; +} from '@/db/server'; import { logger } from '@/lib/server'; export async function syncCurrentUser({ diff --git a/src/components/audit-logs/AuditLogDetails.tsx b/src/components/audit-logs/AuditLogDetails.tsx index 1f10034c88..9f407f7eab 100644 --- a/src/components/audit-logs/AuditLogDetails.tsx +++ b/src/components/audit-logs/AuditLogDetails.tsx @@ -1,46 +1,9 @@ -'use client'; - -import { ArrowRight, Calendar, Clock, User } from 'lucide-react'; import Link from 'next/link'; +import { ArrowRight, Calendar, Clock, User } from 'lucide-react'; import type { AuditLog } from '@/db'; -type AuditLogDetailsProps = { - log: AuditLog; -}; - -const formatValue = (value: unknown) => { - if (value === null || value === undefined) { - return None; - } - - if (Array.isArray(value)) { - return ( - - ); - } - - if (typeof value === 'object') { - return ( -
- {Object.entries(value).map(([key, val]) => ( -
- {key}: - {String(val)} -
- ))} -
- ); - } - - return String(value); -}; - -export const AuditLogDetails = ({ log }: AuditLogDetailsProps) => ( +export const AuditLogDetails = ({ log }: { log: AuditLog }) => (
@@ -78,3 +41,34 @@ export const AuditLogDetails = ({ log }: AuditLogDetailsProps) => (
); + +const formatValue = (value: unknown) => { + if (value === null || value === undefined) { + return None; + } + + if (Array.isArray(value)) { + return ( +
    + {value.map((item, index) => ( +
  • {String(item)}
  • + ))} +
+ ); + } + + if (typeof value === 'object') { + return ( +
+ {Object.entries(value).map(([key, val]) => ( +
+ {key}: + {String(val)} +
+ ))} +
+ ); + } + + return String(value); +}; diff --git a/src/components/audit-logs/AuditLogEntry.tsx b/src/components/audit-logs/AuditLogEntry.tsx index f354924d1a..d569d85b5b 100644 --- a/src/components/audit-logs/AuditLogEntry.tsx +++ b/src/components/audit-logs/AuditLogEntry.tsx @@ -1,26 +1,13 @@ import { formatDistance } from 'date-fns'; import { Settings, Sliders, Users } from 'lucide-react'; -import { type AuditLog, AuditLogTargetType } from '@/db/schema'; +import { type AuditLog, AuditLogTargetType } from '@/db'; type AuditLogEntryProps = { log: AuditLog; onClick: (log: AuditLog) => void; }; -const getIconByType = (type: AuditLogTargetType) => { - switch (type) { - case AuditLogTargetType.PROVIDER_WHITELIST: - return ; - case AuditLogTargetType.DEFAULT_PARAMETERS: - return ; - case AuditLogTargetType.MEMBER_CHANGE: - return ; - default: - return ; - } -}; - export const AuditLogEntry = ({ log, onClick }: AuditLogEntryProps) => (
onClick(log)} @@ -40,3 +27,16 @@ export const AuditLogEntry = ({ log, onClick }: AuditLogEntryProps) => (
); + +const getIconByType = (type: AuditLogTargetType) => { + switch (type) { + case AuditLogTargetType.PROVIDER_WHITELIST: + return ; + case AuditLogTargetType.DEFAULT_PARAMETERS: + return ; + case AuditLogTargetType.MEMBER_CHANGE: + return ; + default: + return ; + } +}; diff --git a/src/db/client.ts b/src/db/db.ts similarity index 100% rename from src/db/client.ts rename to src/db/db.ts diff --git a/src/db/enums.ts b/src/db/enums.ts new file mode 100644 index 0000000000..a6214e0734 --- /dev/null +++ b/src/db/enums.ts @@ -0,0 +1,5 @@ +export enum AuditLogTargetType { + PROVIDER_WHITELIST = 1, + DEFAULT_PARAMETERS = 2, + MEMBER_CHANGE = 3, // TODO: Currently no logs of this type are collected. +} diff --git a/src/db/index.ts b/src/db/index.ts index ad941abdc6..5301973e0f 100644 --- a/src/db/index.ts +++ b/src/db/index.ts @@ -1,2 +1,4 @@ -export * from './client'; -export * from './schema'; +// These can safely be imported in client or server environments. + +export * from './enums'; +export * from './types'; diff --git a/src/db/queries/index.ts b/src/db/queries/index.ts new file mode 100644 index 0000000000..b18bf4caef --- /dev/null +++ b/src/db/queries/index.ts @@ -0,0 +1 @@ +export * from './users'; diff --git a/src/db/queries/users.ts b/src/db/queries/users.ts new file mode 100644 index 0000000000..abbaced5dd --- /dev/null +++ b/src/db/queries/users.ts @@ -0,0 +1,13 @@ +import { inArray } from 'drizzle-orm'; + +import { client as db } from '../db'; +import { users } from '../schema'; +import type { User } from '../types'; + +export const getUsersById = async ( + ids: string[], +): Promise> => + (await db.select().from(users).where(inArray(users.id, ids))).reduce( + (acc, user) => ({ ...acc, [user.id]: user }), + {} as Record, + ); diff --git a/src/db/schema.ts b/src/db/schema.ts index f5aa28e22d..9c5dba4aee 100644 --- a/src/db/schema.ts +++ b/src/db/schema.ts @@ -15,6 +15,8 @@ import { ORGANIZATION_ALLOW_ALL, } from '@/types'; +import { AuditLogTargetType } from './enums'; + /** * users */ @@ -48,10 +50,6 @@ export const userRelations = relations(users, ({ one }) => ({ }), })); -export type User = typeof users.$inferSelect; - -export type CreateUser = Omit; - /** * organizations */ @@ -83,10 +81,6 @@ export const orgsRelations = relations(orgs, ({ many, one }) => ({ }), })); -export type Org = typeof orgs.$inferSelect; - -export type CreateOrg = Omit; - /** * organization_settings */ @@ -122,8 +116,6 @@ export const orgSettingsRelations = relations(orgSettings, ({ one }) => ({ }), })); -export type OrgSettings = typeof orgSettings.$inferSelect; - /** * audit_logs */ @@ -138,7 +130,7 @@ export const auditLogs = pgTable( orgId: text('organization_id') .notNull() .references(() => orgs.id), - targetType: integer('target_type').notNull(), // AuditLogTargetType + targetType: integer('target_type').$type().notNull(), targetId: text('target_id').notNull(), newValue: jsonb('new_value').notNull(), createdAt: timestamp('created_at').defaultNow().notNull(), @@ -162,20 +154,3 @@ export const auditLogsRelations = relations(auditLogs, ({ one }) => ({ references: [orgs.id], }), })); - -export enum AuditLogTargetType { - PROVIDER_WHITELIST = 1, - DEFAULT_PARAMETERS = 2, - MEMBER_CHANGE = 3, // TODO: Currently no logs of this type are collected. -} - -export type AuditLog = typeof auditLogs.$inferSelect & { - targetType: AuditLogTargetType; -}; - -export type CreateAuditLog = Omit< - typeof auditLogs.$inferInsert, - 'id' | 'createdAt' -> & { - targetType: AuditLogTargetType; -}; diff --git a/src/db/server.ts b/src/db/server.ts new file mode 100644 index 0000000000..a7ce9554ed --- /dev/null +++ b/src/db/server.ts @@ -0,0 +1,7 @@ +// These can only be imported in a server (node.js) environment. + +export * from './index'; + +export * from './db'; +export * from './schema'; +export * from './queries'; diff --git a/src/db/types.ts b/src/db/types.ts new file mode 100644 index 0000000000..bded50e8f0 --- /dev/null +++ b/src/db/types.ts @@ -0,0 +1,38 @@ +import type { users, orgs, orgSettings, auditLogs } from './schema'; + +type Generated = 'id' | 'createdAt' | 'updatedAt'; + +/** + * users + */ + +export type User = typeof users.$inferSelect; + +export type CreateUser = Omit; + +/** + * orgs + */ + +export type Org = typeof orgs.$inferSelect; + +export type CreateOrg = Omit; + +/** + * orgSettings + */ + +export type OrgSettings = typeof orgSettings.$inferSelect; + +export type CreateOrgSettings = Omit< + typeof orgSettings.$inferInsert, + Generated +>; + +/** + * auditLogs + */ + +export type AuditLog = typeof auditLogs.$inferSelect; + +export type CreateAuditLog = Omit; diff --git a/vitest-global-setup.ts b/vitest-global-setup.ts index 8f043cc9c7..9628c09112 100644 --- a/vitest-global-setup.ts +++ b/vitest-global-setup.ts @@ -1,7 +1,7 @@ import { sql } from 'drizzle-orm'; import { Env } from '@/lib/server'; -import { testDb, disconnect } from '@/db'; +import { testDb, disconnect } from '@/db/server'; async function resetTestDatabase() { const db = testDb!;