a72d42c6d8
Integrates Gemini 3.1 Pro for improved Forex chart analysis, incorporating detailed instructions on Smart Money Concepts, liquidity, and various confirmation indicators like Volume Delta, ATR, EMA, RSI, and VWAP. Also includes image compression and cleanup utilities for testimonial uploads to manage storage efficiently. Firestore rules for signal data have been updated to accommodate longer strings for pair and timeframe.
131 lines
4.9 KiB
Plaintext
131 lines
4.9 KiB
Plaintext
rules_version = '2';
|
|
service cloud.firestore {
|
|
match /databases/{database}/documents {
|
|
// 0. Global Safety Net
|
|
match /{document=**} {
|
|
allow read, write: if false;
|
|
}
|
|
|
|
// --- Helpers ---
|
|
function isSignedIn() {
|
|
return request.auth != null;
|
|
}
|
|
|
|
// Root Admin Check (Fast, No DB Read)
|
|
function isRootAdmin() {
|
|
return isSignedIn() && request.auth.token.email == "fxbrosinvestments00@gmail.com";
|
|
}
|
|
|
|
// Role-based Admin Check (Slower, DB Read)
|
|
// IMPORTANT: Avoid using this in rules for the 'users' collection itself to prevent recursion.
|
|
function isDbAdmin() {
|
|
return isSignedIn() &&
|
|
(
|
|
isRootAdmin() ||
|
|
(
|
|
exists(/databases/$(database)/documents/users/$(request.auth.uid)) &&
|
|
get(/databases/$(database)/documents/users/$(request.auth.uid)).data.get('isAdmin', false) == true
|
|
)
|
|
);
|
|
}
|
|
|
|
function isOwner(userId) {
|
|
return isSignedIn() && request.auth.uid == userId;
|
|
}
|
|
|
|
function isValidId(id) {
|
|
return id is string && id.size() <= 128 && id.matches('^[a-zA-Z0-9_\\-]+$');
|
|
}
|
|
|
|
function incoming() {
|
|
return request.resource.data;
|
|
}
|
|
|
|
function existing() {
|
|
return resource.data;
|
|
}
|
|
|
|
// --- Users Collection ---
|
|
match /users/{userId} {
|
|
// Use isRootAdmin here instead of isDbAdmin to prevent recursion
|
|
allow get: if isOwner(userId) || isRootAdmin();
|
|
allow list: if isRootAdmin() || isDbAdmin();
|
|
|
|
function isValidUser(data) {
|
|
return data.keys().hasAll(['uid', 'email', 'name', 'isPremium', 'isAdmin', 'plan', 'analysisLimit', 'isApproved'])
|
|
&& data.uid is string && data.uid == request.auth.uid
|
|
&& data.email is string && data.email == request.auth.token.email
|
|
&& data.name is string && data.name.size() <= 100
|
|
&& (data.isPremium is bool || data.isPremium == null)
|
|
&& (data.isAdmin is bool || data.isAdmin == null)
|
|
&& data.plan in ['basic', 'pro', 'elite', 'lifetime']
|
|
&& (data.analysisLimit is int || data.analysisLimit == null)
|
|
&& (data.isApproved is bool || data.isApproved == null);
|
|
}
|
|
|
|
allow create: if isOwner(userId)
|
|
&& isValidUser(incoming())
|
|
// Security: Root Admin can create anyone, others created as non-privileged
|
|
&& (incoming().isAdmin == false || isRootAdmin())
|
|
&& (incoming().isApproved == false || isRootAdmin());
|
|
|
|
allow update: if (
|
|
isRootAdmin() || isDbAdmin() ||
|
|
(isOwner(userId) && incoming().diff(existing()).affectedKeys().hasOnly(['name']))
|
|
);
|
|
|
|
allow delete: if isRootAdmin();
|
|
}
|
|
|
|
// --- Signals Collection ---
|
|
match /signals/{signalId} {
|
|
allow get: if isSignedIn() && (resource.data.userId == request.auth.uid || isDbAdmin());
|
|
allow list: if isSignedIn() && resource.data.userId == request.auth.uid;
|
|
allow list: if isDbAdmin();
|
|
|
|
function isValidSignal(data) {
|
|
return data.keys().hasAll(['userId', 'pair', 'timeframe', 'decision', 'score', 'timestamp', 'result'])
|
|
&& data.userId == request.auth.uid
|
|
&& data.pair is string && data.pair.size() <= 100
|
|
&& data.timeframe is string && data.timeframe.size() <= 50
|
|
&& data.decision is string
|
|
&& data.score is int && data.score >= 0 && data.score <= 100
|
|
&& data.timestamp is int
|
|
&& data.result is string;
|
|
}
|
|
|
|
allow create: if isSignedIn()
|
|
&& isValidSignal(incoming())
|
|
// Check approval status (Requires DB read)
|
|
&& exists(/databases/$(database)/documents/users/$(request.auth.uid))
|
|
&& get(/databases/$(database)/documents/users/$(request.auth.uid)).data.isApproved == true;
|
|
|
|
allow update, delete: if isDbAdmin();
|
|
}
|
|
|
|
// --- Testimonials Collection ---
|
|
match /testimonials/{testimonialId} {
|
|
allow read: if true;
|
|
|
|
function isValidTestimonial(data) {
|
|
return data.keys().hasAll(['userId', 'userName', 'text', 'timestamp', 'imageUrls'])
|
|
&& (data.userId == request.auth.uid || isDbAdmin())
|
|
&& data.userName is string && data.userName.size() <= 100
|
|
&& data.text is string && data.text.size() <= 500
|
|
&& data.timestamp is int
|
|
&& data.imageUrls is list && data.imageUrls.size() <= 10
|
|
&& (data.imageUrls.size() == 0 || data.imageUrls[0] is string);
|
|
}
|
|
|
|
allow create: if isSignedIn() && isValidTestimonial(incoming());
|
|
allow update, delete: if isDbAdmin();
|
|
}
|
|
// --- Settings Collection ---
|
|
match /settings/{settingId} {
|
|
allow read: if true;
|
|
allow update, create, delete: if isDbAdmin();
|
|
}
|
|
}
|
|
}
|
|
|