| import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs'; |
| import path from 'path'; |
|
|
| export type SavedFoodReview = { |
| id: string; |
| userEmail: string; |
| submissionId: string; |
| campaignSlug: string; |
| placeName: string; |
| dishName: string; |
| privacy: 'private' | 'public'; |
| createdAt: number; |
| }; |
|
|
| const LOCAL_DATA_DIR = path.join(process.cwd(), '.local-review-data'); |
| const USER_REVIEWS_FILE = path.join(LOCAL_DATA_DIR, 'user-reviews.json'); |
|
|
| function ensureLocalDir() { |
| mkdirSync(LOCAL_DATA_DIR, { recursive: true }); |
| } |
|
|
| function readAllReviews(): SavedFoodReview[] { |
| if (!existsSync(USER_REVIEWS_FILE)) return []; |
| try { |
| const parsed = JSON.parse(readFileSync(USER_REVIEWS_FILE, 'utf8')) as unknown; |
| if (!Array.isArray(parsed)) return []; |
| return parsed.filter(isSavedReview); |
| } catch { |
| return []; |
| } |
| } |
|
|
| function writeAllReviews(reviews: SavedFoodReview[]) { |
| ensureLocalDir(); |
| writeFileSync(USER_REVIEWS_FILE, `${JSON.stringify(reviews, null, 2)}\n`, 'utf8'); |
| } |
|
|
| function isSavedReview(value: unknown): value is SavedFoodReview { |
| const maybe = value as Partial<SavedFoodReview>; |
| return ( |
| typeof maybe.id === 'string' && |
| typeof maybe.userEmail === 'string' && |
| typeof maybe.submissionId === 'string' && |
| typeof maybe.campaignSlug === 'string' && |
| typeof maybe.placeName === 'string' && |
| typeof maybe.dishName === 'string' && |
| (maybe.privacy === 'private' || maybe.privacy === 'public') && |
| typeof maybe.createdAt === 'number' |
| ); |
| } |
|
|
| function reviewId(userEmail: string, submissionId: string) { |
| return `${userEmail.toLowerCase()}:${submissionId}`; |
| } |
|
|
| export function saveReviewForUser(input: { |
| userEmail: string; |
| submissionId: string; |
| campaignSlug: string; |
| placeName?: string | null; |
| dishName?: string | null; |
| }) { |
| const userEmail = input.userEmail.trim().toLowerCase(); |
| if (!userEmail || !input.submissionId) return null; |
|
|
| const reviews = readAllReviews(); |
| const id = reviewId(userEmail, input.submissionId); |
| const existingIndex = reviews.findIndex((review) => review.id === id); |
| const review: SavedFoodReview = { |
| id, |
| userEmail, |
| submissionId: input.submissionId, |
| campaignSlug: input.campaignSlug, |
| placeName: input.placeName?.trim() || 'Foodstar review', |
| dishName: input.dishName?.trim() || 'Micro food review', |
| privacy: 'private', |
| createdAt: existingIndex >= 0 ? reviews[existingIndex]!.createdAt : Date.now(), |
| }; |
|
|
| if (existingIndex >= 0) reviews[existingIndex] = review; |
| else reviews.push(review); |
| writeAllReviews(reviews); |
| return review; |
| } |
|
|
| export function listReviewsForUser(userEmail: string) { |
| const normalized = userEmail.trim().toLowerCase(); |
| return readAllReviews() |
| .filter((review) => review.userEmail === normalized) |
| .sort((a, b) => b.createdAt - a.createdAt); |
| } |
|
|