feat(*): first mvp

This commit is contained in:
h
2026-01-20 21:54:48 +01:00
parent b9703da2fc
commit ec17f5e0fd
52 changed files with 2599 additions and 576 deletions

View File

@@ -0,0 +1,129 @@
import { v } from 'convex/values';
import { mutation, query } from './_generated/server';
const DEFAULT_MODEL = 'gemini-3-pro-preview';
export const getById = query({
args: { userId: v.id('users') },
returns: v.union(
v.object({
_id: v.id('users'),
_creationTime: v.number(),
telegramId: v.int64(),
telegramChatId: v.optional(v.int64()),
geminiApiKey: v.optional(v.string()),
systemPrompt: v.optional(v.string()),
followUpPrompt: v.optional(v.string()),
model: v.string(),
followUpModel: v.optional(v.string()),
activeChatId: v.optional(v.id('chats'))
}),
v.null()
),
handler: async (ctx, args) => {
return await ctx.db.get(args.userId);
}
});
export const getByTelegramId = query({
args: { telegramId: v.int64() },
returns: v.union(
v.object({
_id: v.id('users'),
_creationTime: v.number(),
telegramId: v.int64(),
telegramChatId: v.optional(v.int64()),
geminiApiKey: v.optional(v.string()),
systemPrompt: v.optional(v.string()),
followUpPrompt: v.optional(v.string()),
model: v.string(),
followUpModel: v.optional(v.string()),
activeChatId: v.optional(v.id('chats'))
}),
v.null()
),
handler: async (ctx, args) => {
return await ctx.db
.query('users')
.withIndex('by_telegram_id', (q) => q.eq('telegramId', args.telegramId))
.unique();
}
});
export const getOrCreate = mutation({
args: { telegramId: v.int64(), telegramChatId: v.optional(v.int64()) },
returns: v.id('users'),
handler: async (ctx, args) => {
const existing = await ctx.db
.query('users')
.withIndex('by_telegram_id', (q) => q.eq('telegramId', args.telegramId))
.unique();
if (existing) {
if (args.telegramChatId && existing.telegramChatId !== args.telegramChatId) {
await ctx.db.patch(existing._id, { telegramChatId: args.telegramChatId });
}
return existing._id;
}
return await ctx.db.insert('users', {
telegramId: args.telegramId,
telegramChatId: args.telegramChatId,
model: DEFAULT_MODEL
});
}
});
export const setApiKey = mutation({
args: { userId: v.id('users'), apiKey: v.string() },
returns: v.null(),
handler: async (ctx, args) => {
await ctx.db.patch(args.userId, { geminiApiKey: args.apiKey });
return null;
}
});
export const setSystemPrompt = mutation({
args: { userId: v.id('users'), prompt: v.string() },
returns: v.null(),
handler: async (ctx, args) => {
await ctx.db.patch(args.userId, { systemPrompt: args.prompt });
return null;
}
});
export const setFollowUpPrompt = mutation({
args: { userId: v.id('users'), prompt: v.string() },
returns: v.null(),
handler: async (ctx, args) => {
await ctx.db.patch(args.userId, { followUpPrompt: args.prompt });
return null;
}
});
export const setModel = mutation({
args: { userId: v.id('users'), model: v.string() },
returns: v.null(),
handler: async (ctx, args) => {
await ctx.db.patch(args.userId, { model: args.model });
return null;
}
});
export const setFollowUpModel = mutation({
args: { userId: v.id('users'), model: v.string() },
returns: v.null(),
handler: async (ctx, args) => {
await ctx.db.patch(args.userId, { followUpModel: args.model });
return null;
}
});
export const setActiveChat = mutation({
args: { userId: v.id('users'), chatId: v.id('chats') },
returns: v.null(),
handler: async (ctx, args) => {
await ctx.db.patch(args.userId, { activeChatId: args.chatId });
return null;
}
});