|
1 | 1 | import type { Session, ForumEdition, ForumData, DietaryPreference } from "@shared/schema"; |
2 | 2 | import { getMicrosoftGraphService } from "./microsoft-graph"; |
| 3 | +import { db } from "./db"; |
| 4 | +import { dietaryPreferences } from "./db/schema"; |
| 5 | +import { eq, and } from "drizzle-orm"; |
3 | 6 |
|
4 | 7 | export interface IStorage { |
5 | 8 | getForumData(): Promise<ForumData>; |
@@ -28,9 +31,6 @@ export class GraphStorage implements IStorage { |
28 | 31 | private lastGraphAttempt: Date | null = null; |
29 | 32 | private readonly MAX_FAILURES = 3; |
30 | 33 | private readonly RETRY_DELAY_MS = 60000; |
31 | | - |
32 | | - // In-memory storage for dietary preferences (sessionId -> email -> preference) |
33 | | - private dietaryPreferences: Map<string, Map<string, DietaryPreference>> = new Map(); |
34 | 34 |
|
35 | 35 | private shouldTryGraph(): boolean { |
36 | 36 | if (this.graphFailures >= this.MAX_FAILURES) { |
@@ -155,41 +155,85 @@ export class GraphStorage implements IStorage { |
155 | 155 |
|
156 | 156 | async setDietaryPreference(sessionId: string, email: string, name: string, preference: string): Promise<void> { |
157 | 157 | const normalizedEmail = email.toLowerCase(); |
158 | | - if (!this.dietaryPreferences.has(sessionId)) { |
159 | | - this.dietaryPreferences.set(sessionId, new Map()); |
160 | | - } |
161 | | - const sessionPrefs = this.dietaryPreferences.get(sessionId)!; |
| 158 | + const trimmedPreference = preference.trim(); |
162 | 159 |
|
163 | | - if (preference.trim() === "") { |
164 | | - // Remove preference if empty |
165 | | - sessionPrefs.delete(normalizedEmail); |
| 160 | + const existing = await db.select() |
| 161 | + .from(dietaryPreferences) |
| 162 | + .where(and( |
| 163 | + eq(dietaryPreferences.sessionId, sessionId), |
| 164 | + eq(dietaryPreferences.email, normalizedEmail) |
| 165 | + )) |
| 166 | + .limit(1); |
| 167 | + |
| 168 | + if (trimmedPreference === "") { |
| 169 | + if (existing.length > 0) { |
| 170 | + await db.delete(dietaryPreferences) |
| 171 | + .where(and( |
| 172 | + eq(dietaryPreferences.sessionId, sessionId), |
| 173 | + eq(dietaryPreferences.email, normalizedEmail) |
| 174 | + )); |
| 175 | + } |
| 176 | + } else if (existing.length > 0) { |
| 177 | + await db.update(dietaryPreferences) |
| 178 | + .set({ name, preference: trimmedPreference, submittedAt: new Date() }) |
| 179 | + .where(and( |
| 180 | + eq(dietaryPreferences.sessionId, sessionId), |
| 181 | + eq(dietaryPreferences.email, normalizedEmail) |
| 182 | + )); |
166 | 183 | } else { |
167 | | - sessionPrefs.set(normalizedEmail, { |
168 | | - email: normalizedEmail, |
169 | | - name, |
170 | | - preference: preference.trim(), |
171 | | - submittedAt: new Date().toISOString(), |
172 | | - }); |
| 184 | + await db.insert(dietaryPreferences) |
| 185 | + .values({ sessionId, email: normalizedEmail, name, preference: trimmedPreference }); |
173 | 186 | } |
174 | 187 | } |
175 | 188 |
|
176 | 189 | async getDietaryPreference(sessionId: string, email: string): Promise<DietaryPreference | undefined> { |
177 | 190 | const normalizedEmail = email.toLowerCase(); |
178 | | - return this.dietaryPreferences.get(sessionId)?.get(normalizedEmail); |
| 191 | + const rows = await db.select() |
| 192 | + .from(dietaryPreferences) |
| 193 | + .where(and( |
| 194 | + eq(dietaryPreferences.sessionId, sessionId), |
| 195 | + eq(dietaryPreferences.email, normalizedEmail) |
| 196 | + )) |
| 197 | + .limit(1); |
| 198 | + |
| 199 | + if (rows.length === 0) return undefined; |
| 200 | + const row = rows[0]; |
| 201 | + return { |
| 202 | + email: row.email, |
| 203 | + name: row.name, |
| 204 | + preference: row.preference, |
| 205 | + submittedAt: row.submittedAt.toISOString(), |
| 206 | + }; |
179 | 207 | } |
180 | 208 |
|
181 | 209 | async getDietaryPreferencesForSession(sessionId: string): Promise<DietaryPreference[]> { |
182 | | - const sessionPrefs = this.dietaryPreferences.get(sessionId); |
183 | | - if (!sessionPrefs) return []; |
184 | | - return Array.from(sessionPrefs.values()); |
| 210 | + const rows = await db.select() |
| 211 | + .from(dietaryPreferences) |
| 212 | + .where(eq(dietaryPreferences.sessionId, sessionId)); |
| 213 | + |
| 214 | + return rows.map(row => ({ |
| 215 | + email: row.email, |
| 216 | + name: row.name, |
| 217 | + preference: row.preference, |
| 218 | + submittedAt: row.submittedAt.toISOString(), |
| 219 | + })); |
185 | 220 | } |
186 | 221 |
|
187 | 222 | async getAllDietaryPreferences(): Promise<Map<string, DietaryPreference[]>> { |
| 223 | + const rows = await db.select().from(dietaryPreferences); |
188 | 224 | const result = new Map<string, DietaryPreference[]>(); |
189 | | - const entries = Array.from(this.dietaryPreferences.entries()); |
190 | | - entries.forEach(([sessionId, prefs]) => { |
191 | | - result.set(sessionId, Array.from(prefs.values())); |
192 | | - }); |
| 225 | + |
| 226 | + for (const row of rows) { |
| 227 | + const prefs = result.get(row.sessionId) || []; |
| 228 | + prefs.push({ |
| 229 | + email: row.email, |
| 230 | + name: row.name, |
| 231 | + preference: row.preference, |
| 232 | + submittedAt: row.submittedAt.toISOString(), |
| 233 | + }); |
| 234 | + result.set(row.sessionId, prefs); |
| 235 | + } |
| 236 | + |
193 | 237 | return result; |
194 | 238 | } |
195 | 239 | } |
|
0 commit comments