Files
desartstudio95 2d6e1a0809 feat(deriv): integrate Deriv API websocket connectivity
Add Deriv API websocket client, route handling, and Firestore security rules to support automated trading integration.
2026-06-10 14:58:02 -07:00

153 lines
5.7 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 true; // Temporarily allow list to bypass backend worker limitation
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: if isDbAdmin() || (isSignedIn() && resource.data.userId == request.auth.uid && incoming().diff(existing()).affectedKeys().hasOnly(['result']));
allow 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();
}
// Auto Trades Collection
match /users/{userId}/auto_trades/{document=**} {
allow read, write: if isOwner(userId) || isDbAdmin();
}
// Auto Trades Results Collection
match /users/{userId}/auto_trades_results/{document=**} {
allow read, write: if isOwner(userId) || isDbAdmin();
}
// Global Auto Trades Collection
match /global_auto_trades/{document=**} {
allow read, write: if true;
}
// --- Telegram Active Signals (Usado pelo background worker) ---
match /telegram_active_signals/{document=**} {
allow read, write: if true;
}
}
}