diff --git a/src/utils/intent-admin.server.ts b/src/utils/intent-admin.server.ts index 20a92268d..4c733d3cf 100644 --- a/src/utils/intent-admin.server.ts +++ b/src/utils/intent-admin.server.ts @@ -246,7 +246,11 @@ export async function triggerIntentProcess() { // Trigger: retry a specific failed version by id // --------------------------------------------------------------------------- -export async function retryIntentVersion({ data }: { data: any }) { +export async function retryIntentVersion({ + data, +}: { + data: { versionId: number } +}) { await requireCapability({ data: { capability: 'admin' } }) const version = await db.query.intentPackageVersions.findFirst({ @@ -395,7 +399,7 @@ export async function discoverViaGitHub() { // Useful for packages that ship skills but haven't yet published the keyword. // --------------------------------------------------------------------------- -export async function seedIntentPackage({ data }: { data: any }) { +export async function seedIntentPackage({ data }: { data: { name: string } }) { await requireCapability({ data: { capability: 'admin' } }) const packument = await fetchPackument(data.name) @@ -426,7 +430,11 @@ export async function seedIntentPackage({ data }: { data: any }) { // Delete a package and all its versions/skills // --------------------------------------------------------------------------- -export async function deleteIntentPackage({ data }: { data: any }) { +export async function deleteIntentPackage({ + data, +}: { + data: { name: string } +}) { await requireCapability({ data: { capability: 'admin' } }) await db.delete(intentPackages).where(eq(intentPackages.name, data.name)) diff --git a/src/utils/stats-admin.server.ts b/src/utils/stats-admin.server.ts index 7904a874d..1f82bfef7 100644 --- a/src/utils/stats-admin.server.ts +++ b/src/utils/stats-admin.server.ts @@ -33,7 +33,11 @@ export async function listGitHubStatsCache() { /** * Refresh GitHub stats for a specific repo or org */ -export async function refreshGitHubStats({ data }: { data: any }) { +export async function refreshGitHubStats({ + data, +}: { + data: { cacheKey: string } +}) { await requireCapability({ data: { capability: 'admin' } }) const isOrg = data.cacheKey.startsWith('org:') @@ -158,7 +162,7 @@ export async function refreshAllGitHubStats() { } } -export async function refreshAllNpmStats({ data }: { data: any }) { +export async function refreshAllNpmStats({ data }: { data: { org: string } }) { console.log(`[Admin] refreshAllNpmStats handler called with org: ${data.org}`) await requireCapability({ data: { capability: 'admin' } }) diff --git a/src/utils/stats.server.ts b/src/utils/stats.server.ts index 1fc8fc94d..9bf2d4654 100644 --- a/src/utils/stats.server.ts +++ b/src/utils/stats.server.ts @@ -888,7 +888,11 @@ export async function fetchNpmDownloadsBulk({ data }: { data: unknown }) { * Chunk format: YYYY (e.g., "2023" means Jan 1 - Dec 31, 2023) * Special chunk "current" means current year to date */ -export async function fetchNpmDownloadChunk({ data }: { data: any }) { +export async function fetchNpmDownloadChunk({ + data, +}: { + data: { packageName: string; year: string } +}) { const { packageName, year } = data // NPM download statistics only go back to January 10, 2015 diff --git a/src/utils/users.server.ts b/src/utils/users.server.ts index 79bb6b5c9..e1c63c14a 100644 --- a/src/utils/users.server.ts +++ b/src/utils/users.server.ts @@ -9,6 +9,23 @@ import { type Capability, type SignupSource } from '~/db/types' type UserRecord = InferSelectModel +// Follows the valibot schema in `users.functions.ts`, which validates every +// call before delegating here — deliberately looser, since the schema's +// bounds have no type-level equivalent and `useEffectiveCapabilities` stays +// optional to keep the `?? true` below meaningful. +type ListUsersInput = { + pagination: { limit: number; page?: number } + emailFilter?: string + nameFilter?: string + capabilityFilter?: Array + noCapabilitiesFilter?: boolean + adsDisabledFilter?: boolean + interestedInHidingAdsFilter?: boolean + useEffectiveCapabilities?: boolean + sortBy?: string + sortDir?: 'asc' | 'desc' +} + // Helper function to validate user capability // Optimized: getEffectiveCapabilities is already called in getAuthenticatedUser, // so we can reuse the capabilities from there @@ -28,7 +45,7 @@ async function requireCapability({ } // Server function wrapper for listUsers -export async function listUsers({ data }: { data: any }) { +export async function listUsers({ data }: { data: ListUsersInput }) { if (!data || !data.pagination) { throw new Error('Missing required') } @@ -287,7 +304,7 @@ export async function listUsers({ data }: { data: any }) { } // Get a single user by ID (admin only) -export async function getUser({ data }: { data: any }) { +export async function getUser({ data }: { data: { userId: string } }) { await requireCapability({ data: { capability: 'admin' } }) const user = await db.query.users.findFirst({ @@ -318,7 +335,11 @@ export async function getUser({ data }: { data: any }) { } // Server function wrapper for updateAdPreference -export async function updateAdPreference({ data }: { data: any }) { +export async function updateAdPreference({ + data, +}: { + data: { adsDisabled: boolean } +}) { const user = await getAuthenticatedUser() // Validate disableAds capability @@ -333,7 +354,11 @@ export async function updateAdPreference({ data }: { data: any }) { } // Server function wrapper for setInterestedInHidingAds -export async function setInterestedInHidingAds({ data }: { data: any }) { +export async function setInterestedInHidingAds({ + data, +}: { + data: { interested: boolean } +}) { const user = await getAuthenticatedUser() // Verify user exists @@ -357,7 +382,11 @@ export async function setInterestedInHidingAds({ data }: { data: any }) { } // Server function to update user's last used framework preference -export async function updateLastUsedFramework({ data }: { data: any }) { +export async function updateLastUsedFramework({ + data, +}: { + data: { framework: string } +}) { const user = await getAuthenticatedUser() await db @@ -415,7 +444,11 @@ export async function addUserSignupSource({ } // Server function wrapper for updateUserCapabilities (admin only) -export async function updateUserCapabilities({ data }: { data: any }) { +export async function updateUserCapabilities({ + data, +}: { + data: { userId: string; capabilities: Array } +}) { // Validate admin capability const { currentUser } = await requireCapability({ data: { capability: 'admin' }, @@ -454,7 +487,11 @@ export async function updateUserCapabilities({ data }: { data: any }) { } // Server function wrapper for adminSetAdsDisabled (admin only) -export async function adminSetAdsDisabled({ data }: { data: any }) { +export async function adminSetAdsDisabled({ + data, +}: { + data: { userId: string; adsDisabled: boolean } +}) { // Validate admin capability const { currentUser } = await requireCapability({ data: { capability: 'admin' }, @@ -492,7 +529,11 @@ export async function adminSetAdsDisabled({ data }: { data: any }) { } // Server function wrapper for bulkUpdateUserCapabilities (admin only) -export async function bulkUpdateUserCapabilities({ data }: { data: any }) { +export async function bulkUpdateUserCapabilities({ + data, +}: { + data: { userIds: Array; capabilities: Array } +}) { // Validate admin capability const { currentUser } = await requireCapability({ data: { capability: 'admin' },