chore: security hardening, mobile PWA fix & analysis expansion
- Applied strict RLS and auth validation to tracking/credit services - Set Service Worker to Network First to fix mobile session/loading issues - Expanded Gemini analysis summary to show distilled/bottled/batch info - Updated SQL schema document with hardening policies
This commit is contained in:
@@ -3,6 +3,8 @@
|
||||
import { createServerComponentClient } from '@supabase/auth-helpers-nextjs';
|
||||
import { cookies } from 'next/headers';
|
||||
|
||||
import { checkIsAdmin } from './track-api-usage';
|
||||
|
||||
interface UserCredits {
|
||||
user_id: string;
|
||||
balance: number;
|
||||
@@ -34,6 +36,17 @@ export async function getUserCredits(userId: string): Promise<UserCredits | null
|
||||
try {
|
||||
const supabase = createServerComponentClient({ cookies });
|
||||
|
||||
// Security check: Only self or admin can view credits
|
||||
const { data: { user: currentUser } } = await supabase.auth.getUser();
|
||||
if (!currentUser) return null;
|
||||
|
||||
const isAdmin = await checkIsAdmin(currentUser.id);
|
||||
|
||||
if (currentUser.id !== userId && !isAdmin) {
|
||||
console.error('Unauthorized credit fetch attempt');
|
||||
return null;
|
||||
}
|
||||
|
||||
const { data, error } = await supabase
|
||||
.from('user_credits')
|
||||
.select('*')
|
||||
@@ -132,6 +145,16 @@ export async function deductCredits(
|
||||
return { success: false, error: 'Could not fetch credit information' };
|
||||
}
|
||||
|
||||
// Security check: Only self or admin can deduct credits
|
||||
const { data: { user: currentUser } } = await supabase.auth.getUser();
|
||||
if (!currentUser) return { success: false, error: 'Nicht authentifiziert' };
|
||||
|
||||
const isAdmin = await checkIsAdmin(currentUser.id);
|
||||
|
||||
if (currentUser.id !== userId && !isAdmin) {
|
||||
return { success: false, error: 'Nicht autorisiert' };
|
||||
}
|
||||
|
||||
const cost = apiType === 'google_search'
|
||||
? credits.google_search_cost
|
||||
: credits.gemini_ai_cost;
|
||||
@@ -140,7 +163,7 @@ export async function deductCredits(
|
||||
if (credits.balance < cost) {
|
||||
return {
|
||||
success: false,
|
||||
error: `Insufficient credits. Need ${cost}, have ${credits.balance}`
|
||||
error: `Nicht genügend Credits. Du benötigst ${cost}, hast aber nur ${credits.balance}.`
|
||||
};
|
||||
}
|
||||
|
||||
@@ -197,6 +220,16 @@ export async function addCredits(
|
||||
try {
|
||||
const supabase = createServerComponentClient({ cookies });
|
||||
|
||||
// Security check
|
||||
const { data: { user: currentUser } } = await supabase.auth.getUser();
|
||||
if (!currentUser) return { success: false, error: 'Nicht authentifiziert' };
|
||||
|
||||
const isAdmin = await checkIsAdmin(currentUser.id);
|
||||
|
||||
if (!isAdmin && adminId) {
|
||||
return { success: false, error: 'Nur Administratoren können Credits anpassen.' };
|
||||
}
|
||||
|
||||
// Get current credits
|
||||
const credits = await getUserCredits(userId);
|
||||
if (!credits) {
|
||||
|
||||
@@ -35,6 +35,13 @@ export async function trackApiUsage(params: TrackApiUsageParams): Promise<{ succ
|
||||
try {
|
||||
const supabase = createServerComponentClient({ cookies });
|
||||
|
||||
// Security check: Ensure user is only tracking their own usage
|
||||
const { data: { user } } = await supabase.auth.getUser();
|
||||
if (!user || user.id !== params.userId) {
|
||||
console.error('Unauthorized API tracking attempt');
|
||||
return { success: false, error: 'Nicht autorisiert' };
|
||||
}
|
||||
|
||||
const { error } = await supabase
|
||||
.from('api_usage')
|
||||
.insert({
|
||||
|
||||
Reference in New Issue
Block a user