kepler-chat/src/lib/backend/convex/user_keys.ts

114 lines
2.6 KiB
TypeScript

import { v } from 'convex/values';
import { Provider } from '../../types';
import { api, internal } from './_generated/api';
import { query } from './_generated/server';
import { mutation } from './functions';
import { providerValidator } from './schema';
import { type SessionObj } from './betterAuth';
export const all = query({
args: {
session_token: v.string(),
},
handler: async (ctx, args) => {
const session = await ctx.runQuery(api.betterAuth.publicGetSession, {
session_token: args.session_token,
});
if (!session) {
throw new Error('Unauthorized');
}
const s = session as SessionObj;
const allKeys = await ctx.db
.query('user_keys')
.withIndex('by_user', (q) => q.eq('user_id', s.userId))
.collect();
return Object.values(Provider).reduce(
(acc, key) => {
acc[key] = allKeys.find((item) => item.provider === key)?.key;
return acc;
},
{} as Record<Provider, string | undefined>
);
},
});
export const get = query({
args: {
provider: providerValidator,
session_token: v.string(),
},
handler: async (ctx, args) => {
const session = await ctx.runQuery(api.betterAuth.publicGetSession, {
session_token: args.session_token,
});
if (!session) {
throw new Error('Unauthorized');
}
const s = session as SessionObj;
const key = await ctx.db
.query('user_keys')
.withIndex('by_provider_user', (q) => q.eq('provider', args.provider).eq('user_id', s.userId))
.first();
return key?.key;
},
});
export const set = mutation({
args: {
provider: providerValidator,
key: v.string(),
session_token: v.string(),
},
handler: async (ctx, args) => {
const session = await ctx.runQuery(internal.betterAuth.getSession, {
sessionToken: args.session_token,
});
if (!session) {
throw new Error('Unauthorized');
}
const existing = await ctx.db
.query('user_keys')
.withIndex('by_provider_user', (q) =>
q.eq('provider', args.provider).eq('user_id', session.userId)
)
.first();
const userKey = { ...args, session_token: undefined, user_id: session.userId };
if (existing) {
await ctx.db.replace(existing._id, userKey);
} else {
await ctx.db.insert('user_keys', userKey);
if (args.provider === Provider.OpenRouter) {
const defaultModels = [
'google/gemini-2.5-flash',
'anthropic/claude-sonnet-4',
'openai/o3-mini',
'deepseek/deepseek-chat-v3-0324:free',
];
await Promise.all(
defaultModels.map((model) =>
ctx.db.insert('user_enabled_models', {
user_id: session.userId,
provider: Provider.OpenRouter,
model_id: model,
pinned: null,
})
)
);
}
}
},
});