Files
mesh-forge/convex/profiles.ts
T

221 lines
5.8 KiB
TypeScript

import { getAuthUserId } from '@convex-dev/auth/server'
import { v } from 'convex/values'
import { mutation, query } from './_generated/server'
export const list = query({
args: {},
handler: async (ctx) => {
const userId = await getAuthUserId(ctx)
if (!userId) return []
return await ctx.db
.query('profiles')
.withIndex('by_user', (q) => q.eq('userId', userId))
.collect()
},
})
export const listPublic = query({
args: {},
handler: async (ctx) => {
// Get all profiles and filter for public ones (isPublic === true or undefined)
// Note: Index query with optional field may not work as expected, so we filter manually
const allProfiles = await ctx.db.query('profiles').collect()
return allProfiles.filter((p) => p.isPublic !== false)
},
})
export const get = query({
args: { id: v.id('profiles') },
handler: async (ctx, args) => {
const profile = await ctx.db.get(args.id)
if (!profile) return null
// Treat undefined as public (backward compatibility)
if (profile.isPublic === false) {
// Check if user owns this profile
const userId = await getAuthUserId(ctx)
if (!userId || profile.userId !== userId) {
return null
}
}
return profile
},
})
export const getTargets = query({
args: { profileId: v.id('profiles') },
handler: async (ctx, args) => {
const profileTargets = await ctx.db
.query('profileTargets')
.withIndex('by_profile', (q) => q.eq('profileId', args.profileId))
.collect()
return profileTargets.map((pt) => pt.target)
},
})
export const getProfileTarget = query({
args: { profileTargetId: v.id('profileTargets') },
handler: async (ctx, args) => {
const profileTarget = await ctx.db.get(args.profileTargetId)
if (!profileTarget) return null
// Get the associated build via profileBuilds
const profileBuild = await ctx.db
.query('profileBuilds')
.withIndex('by_profile_target', (q) =>
q
.eq('profileId', profileTarget.profileId)
.eq('target', profileTarget.target)
)
.first()
const build = profileBuild ? await ctx.db.get(profileBuild.buildId) : null
return {
profileTarget,
build,
}
},
})
export const getFlashCount = query({
args: { profileId: v.id('profiles') },
handler: async (ctx, args) => {
const profileBuilds = await ctx.db
.query('profileBuilds')
.withIndex('by_profile', (q) => q.eq('profileId', args.profileId))
.collect()
let successCount = 0
for (const profileBuild of profileBuilds) {
const build = await ctx.db.get(profileBuild.buildId)
if (build && build.status === 'success') {
successCount++
}
}
return successCount
},
})
export const create = mutation({
args: {
name: v.string(),
targets: v.optional(v.array(v.string())),
config: v.any(),
version: v.string(),
isPublic: v.optional(v.boolean()),
},
handler: async (ctx, args) => {
const userId = await getAuthUserId(ctx)
if (!userId) throw new Error('Unauthorized')
const profileId = await ctx.db.insert('profiles', {
userId,
name: args.name,
config: args.config,
version: args.version,
updatedAt: Date.now(),
isPublic: args.isPublic ?? true,
})
// Create profileTargets entries
if (args.targets) {
for (const target of args.targets) {
await ctx.db.insert('profileTargets', {
profileId,
target,
createdAt: Date.now(),
})
}
}
return profileId
},
})
export const update = mutation({
args: {
id: v.id('profiles'),
name: v.string(),
targets: v.optional(v.array(v.string())),
config: v.any(),
version: v.optional(v.string()),
isPublic: v.optional(v.boolean()),
},
handler: async (ctx, args) => {
const userId = await getAuthUserId(ctx)
if (!userId) throw new Error('Unauthorized')
const profile = await ctx.db.get(args.id)
if (!profile || profile.userId !== userId) {
throw new Error('Unauthorized')
}
// Update profile
await ctx.db.patch(args.id, {
name: args.name,
config: args.config,
version: args.version,
isPublic: args.isPublic,
updatedAt: Date.now(),
})
// Sync profileTargets if targets are provided
if (args.targets !== undefined) {
const newTargets = new Set(args.targets)
const existingProfileTargets = await ctx.db
.query('profileTargets')
.withIndex('by_profile', (q) => q.eq('profileId', args.id))
.collect()
const existingTargets = new Set(
existingProfileTargets.map((pt) => pt.target)
)
// Delete targets that are no longer in the list
for (const profileTarget of existingProfileTargets) {
if (!newTargets.has(profileTarget.target)) {
await ctx.db.delete(profileTarget._id)
}
}
// Add new targets
for (const target of args.targets) {
if (!existingTargets.has(target)) {
await ctx.db.insert('profileTargets', {
profileId: args.id,
target,
createdAt: Date.now(),
})
}
}
}
},
})
export const remove = mutation({
args: { id: v.id('profiles') },
handler: async (ctx, args) => {
const userId = await getAuthUserId(ctx)
if (!userId) throw new Error('Unauthorized')
const profile = await ctx.db.get(args.id)
if (!profile || profile.userId !== userId) {
throw new Error('Unauthorized')
}
// Delete associated profileTargets
const profileTargets = await ctx.db
.query('profileTargets')
.withIndex('by_profile', (q) => q.eq('profileId', args.id))
.collect()
for (const profileTarget of profileTargets) {
await ctx.db.delete(profileTarget._id)
}
await ctx.db.delete(args.id)
},
})