1+ "use server" ;
2+
13import { headers } from "next/headers" ;
24import { getAuthServer } from "./auth" ;
35import { getDrizzle } from "./drizzle" ;
46import { chat , message } from "@/schema/chat" ;
57import { and , asc , eq } from "drizzle-orm" ;
8+ import { Auth } from "better-auth" ;
9+ import { inMemoryCache } from "./cache" ;
610
711export interface CreateChatMessage {
812 role : "user" | "ai" | "error" ;
913 content : string ;
1014}
1115
16+ // cacheに使うキーで、実際のURLではない
17+ const CACHE_KEY_BASE = "https://my-code.utcode.net/chatHistory" ;
18+
19+ interface Context {
20+ drizzle : Awaited < ReturnType < typeof getDrizzle > > ;
21+ auth : Auth ;
22+ userId : string ;
23+ }
24+ /**
25+ * drizzleが初期化されてなければ初期化し、
26+ * authが初期化されてなければ初期化し、
27+ * userIdがなければセッションから取得してセットする。
28+ */
29+ async function initAll ( ctx ?: Partial < Context > ) : Promise < Context > {
30+ if ( ! ctx ) {
31+ ctx = { } ;
32+ }
33+ if ( ! ctx . drizzle ) {
34+ ctx . drizzle = await getDrizzle ( ) ;
35+ }
36+ if ( ! ctx . auth ) {
37+ ctx . auth = await getAuthServer ( ctx . drizzle ) ;
38+ }
39+ if ( ! ctx . userId ) {
40+ const session = await ctx . auth . api . getSession ( {
41+ headers : await headers ( ) ,
42+ } ) ;
43+ if ( ! session ) {
44+ throw new Error ( "Not authenticated" ) ;
45+ }
46+ ctx . userId = session . user . id ;
47+ }
48+ return ctx as Context ;
49+ }
50+ async function getCache ( ) {
51+ if ( "caches" in globalThis ) {
52+ // worker
53+ return await caches . open ( "chatHistory" ) ;
54+ } else {
55+ // nodejs
56+ return inMemoryCache ;
57+ }
58+ }
59+
1260export async function addChat (
1361 docsId : string ,
1462 sectionId : string ,
15- messages : CreateChatMessage [ ]
63+ messages : CreateChatMessage [ ] ,
64+ context ?: Partial < Context >
1665) {
17- const drizzle = await getDrizzle ( ) ;
18- const auth = await getAuthServer ( drizzle ) ;
19- const session = await auth . api . getSession ( { headers : await headers ( ) } ) ;
20- if ( ! session ) {
21- throw new Error ( "Not authenticated" ) ;
22- }
66+ const { drizzle, userId } = await initAll ( context ) ;
2367
2468 const [ newChat ] = await drizzle
2569 . insert ( chat )
2670 . values ( {
27- userId : session . user . id ,
71+ userId,
2872 docsId,
2973 sectionId,
3074 } )
@@ -41,6 +85,14 @@ export async function addChat(
4185 )
4286 . returning ( ) ;
4387
88+ console . log (
89+ `deleting cache for chatHistory/getChat for user ${ userId } and docs ${ docsId } `
90+ ) ;
91+ const cache = await getCache ( ) ;
92+ await cache . delete (
93+ `${ CACHE_KEY_BASE } /getChat?docsId=${ docsId } &userId=${ userId } `
94+ ) ;
95+
4496 return {
4597 ...newChat ,
4698 messages : chatMessages ,
@@ -49,16 +101,14 @@ export async function addChat(
49101
50102export type ChatWithMessages = Awaited < ReturnType < typeof addChat > > ;
51103
52- export async function getChat ( docsId : string ) {
53- const drizzle = await getDrizzle ( ) ;
54- const auth = await getAuthServer ( drizzle ) ;
55- const session = await auth . api . getSession ( { headers : await headers ( ) } ) ;
56- if ( ! session ) {
57- return [ ] ;
58- }
104+ export async function getChat (
105+ docsId : string ,
106+ context ?: Partial < Context >
107+ ) : Promise < ChatWithMessages [ ] > {
108+ const { drizzle, userId } = await initAll ( context ) ;
59109
60110 const chats = await drizzle . query . chat . findMany ( {
61- where : and ( eq ( chat . userId , session . user . id ) , eq ( chat . docsId , docsId ) ) ,
111+ where : and ( eq ( chat . userId , userId ) , eq ( chat . docsId , docsId ) ) ,
62112 with : {
63113 messages : {
64114 orderBy : [ asc ( message . createdAt ) ] ,
@@ -67,8 +117,33 @@ export async function getChat(docsId: string) {
67117 orderBy : [ asc ( chat . createdAt ) ] ,
68118 } ) ;
69119
120+ const cache = await getCache ( ) ;
121+ await cache . put (
122+ `${ CACHE_KEY_BASE } /getChat?docsId=${ docsId } &userId=${ userId } ` ,
123+ new Response ( JSON . stringify ( chats ) , {
124+ headers : { "Cache-Control" : "max-age=86400, s-maxage=86400" } ,
125+ } )
126+ ) ;
70127 return chats ;
71128}
129+ export async function getChatFromCache (
130+ docsId : string ,
131+ context ?: Partial < Context >
132+ ) {
133+ const { drizzle, auth, userId } = await initAll ( context ) ;
134+
135+ const cache = await getCache ( ) ;
136+ const cachedResponse = await cache . match (
137+ `${ CACHE_KEY_BASE } /getChat?docsId=${ docsId } &userId=${ userId } `
138+ ) ;
139+ if ( cachedResponse ) {
140+ console . log ( "Cache hit for chatHistory/getChat" ) ;
141+ const data = ( await cachedResponse . json ( ) ) as ChatWithMessages [ ] ;
142+ return data ;
143+ }
144+ console . log ( "Cache miss for chatHistory/getChat" ) ;
145+ return await getChat ( docsId , { drizzle, auth, userId } ) ;
146+ }
72147
73148export async function migrateChatUser ( oldUserId : string , newUserId : string ) {
74149 const drizzle = await getDrizzle ( ) ;
0 commit comments