You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
179 lines
5.7 KiB
179 lines
5.7 KiB
import { dbGlobal } from "drizzle-pkg/lib/db";
|
|
import { characterSessions, characterMessages } from "drizzle-pkg/lib/schema/character";
|
|
import { eq, desc, asc, and, isNull, sql, max } from "drizzle-orm";
|
|
import type { CharacterSessionRow, CharacterMessageRow } from "./types";
|
|
|
|
export async function createCharacterSession(params: {
|
|
userId?: number | null;
|
|
tempToken?: string | null;
|
|
cardId?: number | null;
|
|
modelId?: number | null;
|
|
expiresAt?: Date | null;
|
|
}): Promise<CharacterSessionRow> {
|
|
const id = `cs_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 10)}`;
|
|
const [row] = await dbGlobal
|
|
.insert(characterSessions)
|
|
.values({
|
|
id,
|
|
userId: params.userId ?? null,
|
|
tempToken: params.tempToken ?? null,
|
|
cardId: params.cardId ?? null,
|
|
modelId: params.modelId ?? null,
|
|
expiresAt: params.expiresAt ?? null,
|
|
})
|
|
.returning();
|
|
return row!;
|
|
}
|
|
|
|
export async function getCharacterSessionById(id: string): Promise<CharacterSessionRow | null> {
|
|
const [row] = await dbGlobal
|
|
.select()
|
|
.from(characterSessions)
|
|
.where(and(eq(characterSessions.id, id), isNull(characterSessions.deletedAt)))
|
|
.limit(1);
|
|
return row ?? null;
|
|
}
|
|
|
|
export async function getCharacterSessionByIdAndUser(
|
|
id: string,
|
|
userId: number | null,
|
|
tempToken: string | null,
|
|
): Promise<CharacterSessionRow | null> {
|
|
const conditions = [eq(characterSessions.id, id), isNull(characterSessions.deletedAt)];
|
|
if (userId) {
|
|
conditions.push(eq(characterSessions.userId, userId));
|
|
} else if (tempToken) {
|
|
conditions.push(eq(characterSessions.tempToken, tempToken));
|
|
} else {
|
|
return null;
|
|
}
|
|
const [row] = await dbGlobal
|
|
.select()
|
|
.from(characterSessions)
|
|
.where(and(...conditions))
|
|
.limit(1);
|
|
return row ?? null;
|
|
}
|
|
|
|
export async function listCharacterSessions(params: {
|
|
userId?: number | null;
|
|
tempToken?: string | null;
|
|
cardId?: number | null;
|
|
page?: number;
|
|
pageSize?: number;
|
|
}): Promise<{ list: CharacterSessionRow[]; total: number; page: number; pageSize: number; totalPages: number }> {
|
|
const { page = 1, pageSize = 30 } = params;
|
|
const conditions = [isNull(characterSessions.deletedAt)];
|
|
if (params.userId) {
|
|
conditions.push(eq(characterSessions.userId, params.userId));
|
|
} else if (params.tempToken) {
|
|
conditions.push(eq(characterSessions.tempToken, params.tempToken));
|
|
} else {
|
|
return { list: [], total: 0, page, pageSize, totalPages: 0 };
|
|
}
|
|
if (params.cardId) {
|
|
conditions.push(eq(characterSessions.cardId, params.cardId));
|
|
}
|
|
|
|
const where = and(...conditions);
|
|
|
|
const [countResult] = await dbGlobal
|
|
.select({ count: sql<number>`count(*)` })
|
|
.from(characterSessions)
|
|
.where(where);
|
|
const total = countResult?.count ?? 0;
|
|
const totalPages = Math.ceil(total / pageSize);
|
|
|
|
const list = await dbGlobal
|
|
.select()
|
|
.from(characterSessions)
|
|
.where(where)
|
|
.orderBy(desc(characterSessions.lastActiveAt))
|
|
.limit(pageSize)
|
|
.offset((page - 1) * pageSize);
|
|
|
|
return { list, total, page, pageSize, totalPages };
|
|
}
|
|
|
|
export async function updateCharacterSession(
|
|
id: string,
|
|
data: {
|
|
title?: string;
|
|
cardId?: number | null;
|
|
modelId?: number | null;
|
|
lastActiveAt?: Date;
|
|
},
|
|
): Promise<void> {
|
|
const updates: Record<string, any> = {};
|
|
if (data.title !== undefined) updates.title = data.title;
|
|
if (data.cardId !== undefined) updates.cardId = data.cardId;
|
|
if (data.modelId !== undefined) updates.modelId = data.modelId;
|
|
if (data.lastActiveAt !== undefined) updates.lastActiveAt = data.lastActiveAt;
|
|
if (Object.keys(updates).length === 0) return;
|
|
await dbGlobal.update(characterSessions).set(updates).where(eq(characterSessions.id, id));
|
|
}
|
|
|
|
export async function softDeleteCharacterSession(id: string): Promise<void> {
|
|
await dbGlobal
|
|
.update(characterSessions)
|
|
.set({ deletedAt: new Date() })
|
|
.where(eq(characterSessions.id, id));
|
|
}
|
|
|
|
export async function getMessagesByCharacterSession(
|
|
sessionId: string,
|
|
params?: { limit?: number; latest?: boolean },
|
|
): Promise<CharacterMessageRow[]> {
|
|
const { limit = 50, latest = false } = params ?? {};
|
|
const baseQuery = () =>
|
|
dbGlobal.select().from(characterMessages).where(eq(characterMessages.sessionId, sessionId));
|
|
|
|
if (latest) {
|
|
const rows = await baseQuery().orderBy(desc(characterMessages.sortOrder)).limit(limit);
|
|
return rows.reverse();
|
|
}
|
|
return baseQuery().orderBy(asc(characterMessages.sortOrder)).limit(limit);
|
|
}
|
|
|
|
export async function getMaxCharacterSortOrder(sessionId: string): Promise<number> {
|
|
const [result] = await dbGlobal
|
|
.select({ maxSort: max(characterMessages.sortOrder) })
|
|
.from(characterMessages)
|
|
.where(eq(characterMessages.sessionId, sessionId));
|
|
return result?.maxSort ?? 0;
|
|
}
|
|
|
|
export async function saveCharacterMessage(params: {
|
|
sessionId: string;
|
|
role: string;
|
|
content: string;
|
|
sortOrder: number;
|
|
}): Promise<CharacterMessageRow> {
|
|
const id = `cm_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 10)}`;
|
|
const [row] = await dbGlobal
|
|
.insert(characterMessages)
|
|
.values({
|
|
id,
|
|
sessionId: params.sessionId,
|
|
role: params.role,
|
|
content: params.content,
|
|
sortOrder: params.sortOrder,
|
|
})
|
|
.returning();
|
|
return row!;
|
|
}
|
|
|
|
export async function countCharacterMessagesByRole(sessionId: string, role: string): Promise<number> {
|
|
const [result] = await dbGlobal
|
|
.select({ cnt: sql<number>`cast(count(*) as integer)` })
|
|
.from(characterMessages)
|
|
.where(and(eq(characterMessages.sessionId, sessionId), eq(characterMessages.role, role)));
|
|
return result?.cnt ?? 0;
|
|
}
|
|
|
|
export async function touchCharacterSession(sessionId: string): Promise<void> {
|
|
await dbGlobal
|
|
.update(characterSessions)
|
|
.set({ lastActiveAt: new Date() })
|
|
.where(eq(characterSessions.id, sessionId));
|
|
}
|
|
|