manifold/firestore.rules
James Grugett 06b7e49e98
[In progress] Server-side feed computation (#106)
* Store view counts & last viewed time

* Schedule updating user recommendations. Compute using tf-idf.

* Update contract's lastBetTime and lastCommentTime on new bets and comments.

* Remove contract's lastUpdatedTime

* Remove folds activity feed

* Implement getFeed cloud function

* Hook up client to use getFeed

* Script to cache viewCounts and lastViewTime

* Batched wait all userRecommendations

* Cache view script runs on all users

* Update user feed each hour and get feed from cache doc.

* Delete view cache script

* Update feed script

* Tweak feed algorithm

* Compute recommendation scores from updateUserFeed

* Disable lastViewedScore factor

* Update lastCommentTime script

* Comment out console.log

* Fix timeout issue by calling new cloud functions with part of the work.

* Listen for contract updates to feed.

* Handle new user: use default feed of top markets this week

* Track lastUpdatedTime

* Tweak logic of calling cloud functions in batches

* Tweak cloud function batching
2022-05-01 11:36:54 -05:00

90 lines
2.9 KiB
Plaintext

rules_version = '2';
// To pick the right project: `firebase projects:list`, then `firebase use <project-name>`
// To deploy: `firebase deploy --only firestore:rules`
service cloud.firestore {
match /databases/{database}/documents {
function isAdmin() {
return request.auth.uid == 'igi2zGXsfxYPgB0DJTXVJVmwCOr2' // Austin
|| request.auth.uid == '5LZ4LgYuySdL1huCWe7bti02ghx2' // James
|| request.auth.uid == 'tlmGNz9kjXc2EteizMORes4qvWl2' // Stephen
|| request.auth.uid == 'IPTOzEqrpkWmEzh6hwvAyY9PqFb2' // Manifold
}
match /users/{userId} {
allow read;
allow update: if resource.data.id == request.auth.uid
&& request.resource.data.diff(resource.data).affectedKeys()
.hasOnly(['bio', 'bannerUrl', 'website', 'twitterHandle', 'discordHandle']);
}
match /private-users/{userId} {
allow read: if resource.data.id == request.auth.uid || isAdmin();
}
match /private-users/{userId}/views/{viewId} {
allow create: if userId == request.auth.uid;
}
match /private-users/{userId}/events/{eventId} {
allow create: if userId == request.auth.uid;
}
match /private-users/{userId}/latency/{loadTimeId} {
allow create: if userId == request.auth.uid;
}
match /private-users/{userId}/cache/feed {
allow read: if userId == request.auth.uid || isAdmin();
}
match /contracts/{contractId} {
allow read;
allow update: if request.resource.data.diff(resource.data).affectedKeys()
.hasOnly(['description', 'closeTime', 'tags', 'lowercaseTags']);
allow update: if isAdmin();
}
match /{somePath=**}/bets/{betId} {
allow read;
}
function commentMatchesUser(userId, comment) {
// it's a bad look if someone can impersonate other ids/names/avatars so check everything
let user = get(/databases/$(database)/documents/users/$(userId));
return comment.userId == userId
&& comment.userName == user.data.name
&& comment.userUsername == user.data.username
&& comment.userAvatarUrl == user.data.avatarUrl;
}
match /{somePath=**}/comments/{commentId} {
allow read;
allow create: if request.auth != null && commentMatchesUser(request.auth.uid, request.resource.data);
}
match /{somePath=**}/answers/{answerId} {
allow read;
}
match /folds/{foldId} {
allow read;
allow update: if request.auth.uid == resource.data.curatorId
&& request.resource.data.diff(resource.data).affectedKeys()
.hasOnly(['name', 'about', 'tags', 'lowercaseTags']);
allow delete: if request.auth.uid == resource.data.curatorId;
}
match /{somePath=**}/followers/{userId} {
allow read;
allow create, update: if request.auth.uid == userId && request.resource.data.userId == userId;
allow delete: if request.auth.uid == userId;
}
match /txns/{txnId} {
allow read;
}
}
}