Initial commit
This commit is contained in:
+157
@@ -0,0 +1,157 @@
|
||||
rules_version = '2';
|
||||
|
||||
service cloud.firestore {
|
||||
match /databases/{database}/documents {
|
||||
|
||||
// --- HELPERS ---
|
||||
function isSignedIn() {
|
||||
return request.auth != null;
|
||||
}
|
||||
|
||||
function isAdmin() {
|
||||
return isSignedIn()
|
||||
&& exists(/databases/$(database)/documents/users/$(request.auth.uid))
|
||||
&& get(/databases/$(database)/documents/users/$(request.auth.uid)).data.role == 'admin';
|
||||
}
|
||||
|
||||
// Toggle like on social_posts: only likedBy + likes may change; count must match list length.
|
||||
function isSocialLikeReactionUpdate() {
|
||||
return isSignedIn()
|
||||
&& request.resource.data.diff(resource.data).affectedKeys().hasOnly(['likedBy', 'likes'])
|
||||
&& request.resource.data.likedBy is list
|
||||
&& request.resource.data.likes == request.resource.data.likedBy.size();
|
||||
}
|
||||
|
||||
// --- USERS ---
|
||||
match /users/{userId} {
|
||||
allow get: if true;
|
||||
allow list: if isAdmin();
|
||||
|
||||
allow create: if isSignedIn() && request.auth.uid == userId;
|
||||
|
||||
allow update: if isAdmin() || (
|
||||
isSignedIn()
|
||||
&& request.auth.uid == userId
|
||||
&& !request.resource.data.diff(resource.data).affectedKeys()
|
||||
.hasAny(['rankLevel', 'role', 'isVerified', 'isBanned'])
|
||||
);
|
||||
|
||||
match /{allPaths=**} {
|
||||
allow read, write: if isSignedIn() && (request.auth.uid == userId || isAdmin());
|
||||
}
|
||||
}
|
||||
|
||||
// --- SOCIAL ---
|
||||
match /social_posts/{postId} {
|
||||
allow read: if true;
|
||||
|
||||
allow create: if isSignedIn()
|
||||
&& request.resource.data.authorId == request.auth.uid;
|
||||
|
||||
allow update: if isAdmin() || (
|
||||
isSignedIn()
|
||||
&& resource.data.authorId == request.auth.uid
|
||||
&& request.resource.data.authorId == resource.data.authorId
|
||||
) || isSocialLikeReactionUpdate();
|
||||
|
||||
allow delete: if isAdmin()
|
||||
|| (isSignedIn() && resource.data.authorId == request.auth.uid);
|
||||
|
||||
match /comments/{commentId} {
|
||||
allow read: if true;
|
||||
allow create: if isSignedIn()
|
||||
&& request.resource.data.uid == request.auth.uid;
|
||||
allow update, delete: if isAdmin()
|
||||
|| (isSignedIn() && resource.data.uid == request.auth.uid);
|
||||
}
|
||||
}
|
||||
|
||||
// --- UPCOMING (Soon tab; artists manage own rows) ---
|
||||
match /upcoming/{announcementId} {
|
||||
allow read: if true;
|
||||
|
||||
allow create: if isSignedIn()
|
||||
&& request.resource.data.authorId == request.auth.uid
|
||||
&& request.resource.data.keys().hasAll([
|
||||
'kind', 'authorId', 'authorName', 'seriesTitle'
|
||||
])
|
||||
&& request.resource.data.kind in ['new_series', 'chapter_drop']
|
||||
&& (
|
||||
request.resource.data.dateTbd == true
|
||||
|| request.resource.data.targetDate is timestamp
|
||||
);
|
||||
|
||||
allow update: if isAdmin() || (
|
||||
isSignedIn()
|
||||
&& resource.data.authorId == request.auth.uid
|
||||
&& request.resource.data.authorId == resource.data.authorId
|
||||
);
|
||||
|
||||
allow delete: if isAdmin()
|
||||
|| (isSignedIn() && resource.data.authorId == request.auth.uid);
|
||||
}
|
||||
|
||||
// --- MANGA & CHAPTERS ---
|
||||
match /manga/{mangaId} {
|
||||
allow read: if true;
|
||||
|
||||
// Anyone signed in can bump read counts; series author can edit portfolio fields.
|
||||
allow update: if isAdmin()
|
||||
|| (isSignedIn()
|
||||
&& request.resource.data.diff(resource.data).affectedKeys().hasOnly(['reads']))
|
||||
|| (isSignedIn()
|
||||
&& resource.data.keys().hasAll(['authorId'])
|
||||
&& resource.data.authorId == request.auth.uid
|
||||
&& request.resource.data.authorId == resource.data.authorId
|
||||
&& request.resource.data.diff(resource.data).affectedKeys()
|
||||
.hasOnly(['coverUrl', 'bannerUrl', 'synopsis', 'aboutArtist', 'socials']));
|
||||
|
||||
allow write: if isAdmin();
|
||||
|
||||
match /chapters/{chapterId} {
|
||||
allow read: if true;
|
||||
|
||||
allow create, update: if isAdmin();
|
||||
|
||||
allow delete: if isAdmin()
|
||||
|| (isSignedIn()
|
||||
&& exists(/databases/$(database)/documents/manga/$(mangaId))
|
||||
&& get(/databases/$(database)/documents/manga/$(mangaId)).data.authorId == request.auth.uid);
|
||||
|
||||
match /page_interactions/{pageId} {
|
||||
allow read: if true;
|
||||
allow create, update: if isSignedIn();
|
||||
|
||||
match /comments/{commentId} {
|
||||
allow read: if true;
|
||||
allow create: if isSignedIn()
|
||||
&& request.resource.data.uid == request.auth.uid;
|
||||
allow update: if isAdmin()
|
||||
|| (isSignedIn() && resource.data.uid == request.auth.uid);
|
||||
allow delete: if isAdmin()
|
||||
|| (isSignedIn() && resource.data.uid == request.auth.uid);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- MARKETPLACE ---
|
||||
match /marketplace/{itemId} {
|
||||
allow read: if true;
|
||||
allow write: if isAdmin();
|
||||
}
|
||||
|
||||
// Sign-up validates a known code via get(); listing all codes is admin-only.
|
||||
match /invite_codes/{code} {
|
||||
allow get: if true;
|
||||
allow list: if isAdmin();
|
||||
allow create, update, delete: if isAdmin();
|
||||
}
|
||||
|
||||
// --- SYSTEM BROADCAST ---
|
||||
match /system/announcement {
|
||||
allow read: if true;
|
||||
allow write: if isAdmin();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user