Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 11 additions & 3 deletions src/utils/intent-admin.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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({
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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))
Expand Down
8 changes: 6 additions & 2 deletions src/utils/stats-admin.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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:')
Expand Down Expand Up @@ -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' } })
Expand Down
6 changes: 5 additions & 1 deletion src/utils/stats.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
57 changes: 49 additions & 8 deletions src/utils/users.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,23 @@ import { type Capability, type SignupSource } from '~/db/types'

type UserRecord = InferSelectModel<typeof users>

// 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<Capability>
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
Expand All @@ -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')
}
Expand Down Expand Up @@ -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({
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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<Capability> }
}) {
// Validate admin capability
const { currentUser } = await requireCapability({
data: { capability: 'admin' },
Expand Down Expand Up @@ -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' },
Expand Down Expand Up @@ -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<string>; capabilities: Array<Capability> }
}) {
// Validate admin capability
const { currentUser } = await requireCapability({
data: { capability: 'admin' },
Expand Down
Loading