-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Co-authored-by: Dhenain Ambroise <ambroise.dhenain@gmail.com> Co-authored-by: Dan Hen <37185115+Dan-Hen@users.noreply.github.com>
- Loading branch information
1 parent
e8a2dc0
commit 7625102
Showing
94 changed files
with
4,701 additions
and
548 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
# XXX Duplicate this file as ".env.local" and fill-in below environment variables. | ||
# This file is used for local development only (localhost). | ||
# You'll need to add those environment variables as "Vercel environment variables" manually, too. | ||
|
||
# Magic Link provides a "publishable key" which is used on the browser (and thus, public). | ||
# Go to https://dashboard.magic.link/ > API Keys > Test "Publishable key" | ||
NEXT_PUBLIC_MAGIC_PUBLISHABLE_KEY= | ||
|
||
# FaunaDB token to generate from FaunaDB, based on the "Public" role. | ||
# Go to https://dashboard.fauna.com/ > Select DB > Shell > Run fql/setup.js (if not done already) | ||
# Go to https://dashboard.fauna.com/ > Select DB > Security > New Key > Role: Public | Name: PUBLIC_SHARED_FAUNABD_TOKEN | ||
NEXT_PUBLIC_SHARED_FAUNABD_TOKEN= | ||
|
||
# Magic Link provides a "secret key" which must only be used from the server. | ||
# Go to https://dashboard.magic.link/ > API Keys > Test "Secret key" | ||
MAGIC_SECRET_KEY= | ||
|
||
# Used by @hapi/iron, must be a string of 32 characters min. Can be any value. | ||
# Changing this secret will invalidate all existing user sessions. (they'll have to log in again) | ||
# You can generate a string using https://passwordsgenerator.net/ (recommended 32 chars, no special chars) | ||
CRYPTO_TOKEN_SECRET= | ||
|
||
# Server secret key for FaunaDB. | ||
# Used to perform actions from the server-side (creating users, etc.) | ||
# Go to https://dashboard.fauna.com/ > Select DB > Security > New Key > Role: Server | Name: FAUNADB_SERVER_SECRET_KEY | ||
FAUNADB_SERVER_SECRET_KEY= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
Create( | ||
Collection('Canvas'), | ||
{ | ||
data: { | ||
owner: Ref(Collection("Users"), "292674252603130373"), | ||
nodes: [], | ||
edges: [], | ||
} | ||
}, | ||
) | ||
|
||
Lambda("ref", Equals( | ||
CurrentIdentity(), | ||
Select(["data", "owner"], Get(Var("ref"))) | ||
)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,148 @@ | ||
// ---------------------- Step 1: Create a "Users" collection ---------------------- | ||
CreateCollection({ name: 'Users' }); | ||
|
||
// ---------------------- Step 2: Create "Canvas" collection ---------------------- | ||
CreateCollection({ name: 'Canvas' }); | ||
|
||
// ---------------------- Step 3: Create indexes ---------------------- | ||
// Index to filter users by email | ||
// Necessary for authentication, to find the user document based on their email | ||
CreateIndex({ | ||
name: 'users_by_email', | ||
source: Collection('Users'), | ||
terms: [ | ||
{ field: ['data', 'email'] }, | ||
], | ||
unique: true, | ||
}); | ||
|
||
// Index to filter canvas by owner | ||
// Necessary for real-time subscription, to retrieve the canvas of the current user | ||
CreateIndex({ | ||
name: 'canvas_by_owner', | ||
source: Collection('Canvas'), | ||
// Needs permission to read the Users, because "owner" is specified in the "terms" and is a Ref to the "Users" collection | ||
permissions: { read: Collection('Users') }, | ||
// Allow to filter by owner ("Users") | ||
terms: [ | ||
{ field: ['data', 'owner'] }, | ||
], | ||
// Index contains the Canvas ref (that's the default behavior and could be omitted) | ||
values: [ | ||
{ field: ['ref'] }, | ||
], | ||
}); | ||
|
||
// ---------------------- Step 4: Create roles ---------------------- | ||
|
||
// The "Editor" role is assigned to all authenticated users | ||
// It is automatically assigned when a user is authenticated, because it defines "membership" to the Users collection | ||
// It is secure because the token is generated upon login on the server-side and stored in a "httpOnly" cookie that can only be read/written on the server-side | ||
// The token is specific to the user and is used on the frontend | ||
// The token only allows the user to read/write documents that belongs to him | ||
CreateRole({ | ||
name: 'Editor', | ||
// All users should be editors (will apply to authenticated users only). | ||
membership: { | ||
resource: Collection('Users'), | ||
}, | ||
privileges: [ | ||
{ | ||
// Editors need read access to the canvas_by_owner index to find their own canvas | ||
resource: Index('canvas_by_owner'), | ||
actions: { | ||
read: true, | ||
}, | ||
}, | ||
{ | ||
resource: Collection('Canvas'), | ||
actions: { | ||
// Editors should be able to read (+ history) of Canvas documents that belongs to them. | ||
read: Query( | ||
Lambda('ref', Equals( | ||
CurrentIdentity(), | ||
Select(['data', 'owner'], Get(Var('ref'))), | ||
)), | ||
), | ||
history_read: Query( | ||
Lambda('ref', Equals( | ||
CurrentIdentity(), | ||
Select(['data', 'owner'], Get(Var('ref'))), | ||
)), | ||
), | ||
// Editors should be able to edit only Canvas documents that belongs to them | ||
write: Lambda( | ||
["oldData", "newData", "ref"], | ||
And( | ||
// The owner in the current data (before writing them) must be the current user | ||
Equals( | ||
CurrentIdentity(), | ||
Select(["data", "owner"], Var("oldData")) | ||
), | ||
// The owner must not change | ||
Equals( | ||
Select(["data", "owner"], Var("oldData")), | ||
Select(["data", "owner"], Var("newData")) | ||
) | ||
) | ||
), | ||
// Editors should be able to create only Canvas documents that belongs to them | ||
create: Lambda("values", Equals( | ||
CurrentIdentity(), | ||
Select(["data", "owner"], Var("values"))) | ||
), | ||
}, | ||
}, | ||
], | ||
}); | ||
|
||
// The "Public" role is assigned to anyone who isn't authenticated | ||
// It doesn't use "membership" (unlike "Editor" role) but a token created manually that doesn't expire | ||
// It is secure because the token only grant access to the special document of id "1", which is shared amongst all guests | ||
// Guests can only read/write this particular document and not any other | ||
CreateRole({ | ||
name: 'Public', | ||
// The public role is meant to be used to generate a token which allows anyone (unauthenticated users) to update the canvas | ||
membership: {}, | ||
privileges: [ | ||
{ | ||
resource: Collection('Canvas'), | ||
actions: { | ||
// Guests should only be allowed to read the Canvas of id "1" | ||
read: Query( | ||
Lambda('ref', | ||
Equals( | ||
'1', | ||
Select(['id'], Var('ref'), | ||
), | ||
), | ||
), | ||
), | ||
// Guests should only be allowed to update the Canvas of id "1" | ||
write: Lambda( | ||
['oldData', 'newData', 'ref'], | ||
Equals( | ||
'1', | ||
Select(['id'], Var('ref')), | ||
), | ||
), | ||
// Guests should only be allowed to create the Canvas of id "1" | ||
create: Lambda('values', | ||
Equals( | ||
'1', | ||
Select(['ref', 'id'], Var('values')), | ||
), | ||
), | ||
// Creating a record with a custom ID requires history_write privilege | ||
// See https://fauna-community.slack.com/archives/CAKNYCHCM/p1615413941454700 | ||
history_write: Lambda( | ||
['ref', 'ts', 'action', 'data'], | ||
Equals( | ||
'1', | ||
Select(['id'], Var('ref')), | ||
), | ||
), | ||
}, | ||
}, | ||
], | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
import React from 'react'; | ||
|
||
export type Props = { | ||
/** | ||
* Color of the dots. | ||
* | ||
* @default white | ||
*/ | ||
fill?: string; | ||
}; | ||
|
||
/** | ||
* An animated composant featuring 3 animated dots "...". | ||
* | ||
* Each dot is animated separately, in alternation. | ||
* Requires animate.css library. | ||
* | ||
* @see https://animate.style | ||
*/ | ||
const Animated3Dots = (props: Props): JSX.Element => { | ||
return ( | ||
<svg | ||
id="AnimatedBubble_svg__Calque_1" | ||
x={0} | ||
y={0} | ||
viewBox="0 0 19 5" | ||
width="20px" | ||
style={{ | ||
overflow: 'visible', | ||
paddingTop: 5, | ||
marginLeft: 5, | ||
}} | ||
xmlSpace="preserve" | ||
fill={'white'} | ||
{...props} | ||
> | ||
<circle | ||
className="animate__animated animate__fadeIn animate__infinite delay-200ms" | ||
cx={2.783} | ||
cy={2.796} | ||
r={2.153} | ||
/> | ||
<circle | ||
className="animate__animated animate__fadeIn animate__infinite delay-400ms" | ||
cx={9.576} | ||
cy={2.796} | ||
r={2.153} | ||
/> | ||
<circle | ||
className="animate__animated animate__fadeIn animate__infinite delay-600ms" | ||
cx={16.369} | ||
cy={2.796} | ||
r={2.153} | ||
/> | ||
</svg> | ||
); | ||
}; | ||
|
||
export default Animated3Dots; |
Oops, something went wrong.
7625102
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Successfully deployed to the following URLs: