Skip to content

Commit 78a6aee

Browse files
Initial commit
0 parents  commit 78a6aee

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

76 files changed

+26013
-0
lines changed

.firebaserc

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"projects": {
3+
"default": "coderev-app"
4+
}
5+
}

.gitignore

+69
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
# Logs
2+
logs
3+
*.log
4+
npm-debug.log*
5+
yarn-debug.log*
6+
yarn-error.log*
7+
firebase-debug.log*
8+
firebase-debug.*.log*
9+
10+
# Firebase cache
11+
.firebase/
12+
13+
# Firebase config
14+
15+
# Uncomment this if you'd like others to create their own Firebase project.
16+
# For a team working on the same Firebase project(s), it is recommended to leave
17+
# it commented so all members can deploy to the same project(s) in .firebaserc.
18+
# .firebaserc
19+
20+
# Runtime data
21+
pids
22+
*.pid
23+
*.seed
24+
*.pid.lock
25+
26+
# Directory for instrumented libs generated by jscoverage/JSCover
27+
lib-cov
28+
29+
# Coverage directory used by tools like istanbul
30+
coverage
31+
32+
# nyc test coverage
33+
.nyc_output
34+
35+
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
36+
.grunt
37+
38+
# Bower dependency directory (https://bower.io/)
39+
bower_components
40+
41+
# node-waf configuration
42+
.lock-wscript
43+
44+
# Compiled binary addons (http://nodejs.org/api/addons.html)
45+
build/Release
46+
47+
# Dependency directories
48+
node_modules/
49+
50+
# Optional npm cache directory
51+
.npm
52+
53+
# Optional eslint cache
54+
.eslintcache
55+
56+
# Optional REPL history
57+
.node_repl_history
58+
59+
# Output of 'npm pack'
60+
*.tgz
61+
62+
# Yarn Integrity file
63+
.yarn-integrity
64+
65+
# dotenv environment variables file
66+
.env
67+
.data/
68+
69+
.DS_store

.vscode/settings.json

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"cSpell.words": [
3+
"Archivable"
4+
]
5+
}

LICENSE

+661
Large diffs are not rendered by default.

README.md

+125
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
# CodeRev
2+
3+
CodeRev is a lightweight tool to help you organize and conduct technical interviews using code reviews rather than leetcode.
4+
5+
## Rationale
6+
7+
In the age of StackOverflow and ChatGPT, is leetcode really the best way to evaluate technical candidates?
8+
9+
*Was it ever?*
10+
11+
Code review as interview has many benefits for any engineering team:
12+
13+
* Understand how candidates interact with isolated parts of your codebase.
14+
* Better reflects day-to-day engineering responsibilities in your team.
15+
* Realistic representation of how a candidate thinks and communicates.
16+
* Open-ended and collaborative; no black and white responses.
17+
18+
## How it works
19+
20+
1. Create a workspace for each of the roles you're screening for.
21+
2. Upload and edit your source files that you'd like the candidates to review.
22+
3. Add candidates; each gets a separate view of the source to work on.
23+
4. Candidates review the code in their workspace and provide comments and feedback.
24+
25+
## Benefits:
26+
27+
Why use CodeRev? Why not just a GitHub repo?
28+
29+
1. **Easy setup**: Lightweight, focused, and simple; just a few clicks to get started.
30+
2. **Isolated**: No exposure of your internal GitHub repos, accounts, and workspaces.
31+
3. **Collaborative**: Review candidate responses with your team and leave your own notes. (Coming soon)
32+
4. **Easy to compare**: See feedback from different candidates to the same code to compare. (Coming soon)
33+
5. **Define timed access**: Automatically release the workspace to your candidate and optionally revoke it. (Coming soon)
34+
35+
## FAQ:
36+
37+
* **Why did you make this tool?** I went through an interview where the process involved reviewing a snippet of code and really enjoyed the experience. I thought it would be great if there was a dedicated tool for this.
38+
* **What's the stack** Nuxt3 (Vue.js) + Quasar Framework + Google Cloud Firebase. Productive, fast, and more or less free.
39+
40+
## Development
41+
42+
Development can be done locally using the Firebase CLI emulators.
43+
44+
1. Install the Firebase CLI tooling for your platform: https://firebase.google.com/docs/cli
45+
2. Make a copy of `web/env.template` as `web/.env` and add your Firebase config.
46+
3. Start the backend
47+
4. Start the frontend
48+
49+
```
50+
# Start the emulators in on console
51+
cd web
52+
yarn # Restore
53+
yarn dev
54+
55+
# Start the backend
56+
firebase emulators:start --only auth,firestore,functions,hosting,storage \
57+
--import .data/firebase --export-on-exit
58+
```
59+
60+
## Deploying
61+
62+
You'll need a Firebase project to deploy:
63+
64+
```
65+
# From web:
66+
yarn generate # This will generate the static routes.
67+
68+
# From the root:
69+
firebase deploy
70+
71+
# Deploy only the hosting (making a front-end change):
72+
firebase deploy --only hosting
73+
```
74+
75+
> 💡 Note: Functions isn't necessary; I started the project thinking I may need it, but you can ignore it and remove it. You won't be charged for it either way if you deploy it with functions.
76+
77+
## Using Functions Framework
78+
79+
Functions framework allows the SSR backend to run on the server. However, this is currently not needed for CodeRev.
80+
81+
To enable, swap the `hosting` configuration:
82+
83+
```json
84+
"source": "web",
85+
"ignore": [
86+
"firebase.json",
87+
"**/.*",
88+
"**/node_modules/**"
89+
],
90+
"frameworksBackend": {
91+
"region": "us-central1"
92+
}
93+
```
94+
95+
## Setting up CORS for Storage
96+
97+
Storage requires that you set up CORS when using a custom domain.
98+
99+
Create the bucket `source.coderev.app` and then follow this guide: https://cloud.google.com/storage/docs/using-cors
100+
101+
Instead of doing it from the command line, you can use the Cloud Shell in console and open an editor in browser.
102+
103+
Create a file `cors.json`
104+
105+
```json
106+
[
107+
{
108+
"origin": ["https://coderev.app"],
109+
"method": ["GET"],
110+
"responseHeader":[
111+
"Access-Control-Allow-Origin"
112+
],
113+
"maxAgeSeconds": 3600
114+
}
115+
]
116+
```
117+
118+
From the console, run:
119+
120+
```
121+
gcloud storage buckets update gs://source.coderev.app --cors-file=cors.json
122+
```
123+
124+
125+

_firebase/firestore.indexes.json

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
{
2+
"indexes": [
3+
{
4+
"collectionGroup": "candidates",
5+
"queryScope": "COLLECTION",
6+
"fields": [
7+
{ "fieldPath": "workspaceUid", "order": "ASCENDING" },
8+
{ "fieldPath": "uid", "order": "ASCENDING" },
9+
{ "fieldPath": "email", "order": "ASCENDING" },
10+
{ "fieldPath": "createdAtUtc", "order": "DESCENDING" }
11+
]
12+
},
13+
{
14+
"collectionGroup": "candidates",
15+
"queryScope": "COLLECTION",
16+
"fields": [
17+
{ "fieldPath": "workspaceUid", "order": "ASCENDING" },
18+
{ "fieldPath": "createdAtUtc", "order": "DESCENDING" }
19+
]
20+
}
21+
],
22+
"fieldOverrides": [
23+
{
24+
"collectionGroup": "workspaces",
25+
"fieldPath": "sources",
26+
"indexes": []
27+
},
28+
{
29+
"collectionGroup": "candidates",
30+
"fieldPath": "sources",
31+
"indexes": []
32+
},
33+
{
34+
"collectionGroup": "candidates",
35+
"fieldPath": "comments",
36+
"indexes": []
37+
}
38+
]
39+
}

_firebase/firestore.rules

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
rules_version = '2';
2+
3+
service cloud.firestore {
4+
match /databases/{database}/documents {
5+
match /profiles/{profile} {
6+
allow read, create: if request.auth != null
7+
8+
allow update: if request.auth.uid == request.resource.id
9+
}
10+
11+
match /workspaces/{workspace} {
12+
allow read, update, delete: if request.auth != null
13+
&& request.auth.uid in resource.data.collaborators
14+
15+
allow create: if request.auth != null
16+
}
17+
18+
match /candidates/{candidate} {
19+
// Allow read and update by the user that the candidate review is assigned
20+
// to or if the user is a collaborator on the workspace referenced by the
21+
// review.
22+
allow read, update: if request.auth != null
23+
&& (request.auth.token.email == resource.data.email
24+
|| request.auth.uid in get(/databases/$(database)/documents/workspaces/$(resource.data.workspaceUid)).data.collaborators)
25+
26+
// Only allow delete if the user is referenced on the workspace document.
27+
allow delete: if request.auth != null
28+
&& request.auth.uid in get(/databases/$(database)/documents/workspaces/$(resource.data.workspaceUid)).data.collaborators
29+
30+
// Allow create for the workspace if the user is a collaborator on the
31+
// referenced workspace.
32+
allow create: if request.auth != null
33+
&& request.auth.uid in get(/databases/$(database)/documents/workspaces/$(request.resource.data.workspaceUid)).data.collaborators
34+
}
35+
}
36+
}

_firebase/remoteconfig.template.json

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{}

_firebase/storage.rules

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
rules_version = '2';
2+
3+
// Craft rules based on data in your Firestore database
4+
// allow write: if firestore.get(
5+
// /databases/(default)/documents/users/$(request.auth.uid)).data.isAdmin;
6+
service firebase.storage {
7+
match /b/{bucket}/o {
8+
match /{allPaths=**} {
9+
allow read, write: if false;
10+
}
11+
}
12+
13+
// The ruleset for the source bucket.
14+
match /b/source.coderev.app/o {
15+
match /{workspaceUid}/{source} {
16+
allow write: if request.auth != null
17+
&& request.auth.uid in firestore.get(/databases/(default)/documents/workspaces/$(workspaceUid)).data.collaborators
18+
19+
allow read: if request.auth != null
20+
}
21+
}
22+
}

build-deploy.sh

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# Generate to build static assets
2+
yarn --cwd web generate
3+
4+
# Deploy hosting only.
5+
firebase deploy --only hosting
6+

firebase.json

+77
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
{
2+
"functions": [
3+
{
4+
"source": "functions",
5+
"codebase": "default",
6+
"ignore": [
7+
"node_modules",
8+
".git",
9+
"firebase-debug.log",
10+
"firebase-debug.*.log"
11+
],
12+
"predeploy": [
13+
"npm --prefix \"$RESOURCE_DIR\" run lint",
14+
"npm --prefix \"$RESOURCE_DIR\" run build"
15+
]
16+
}
17+
],
18+
"firestore": {
19+
"rules": "_firebase/firestore.rules",
20+
"indexes": "_firebase/firestore.indexes.json"
21+
},
22+
"hosting": {
23+
"public": "web/.output/public",
24+
"ignore": ["firebase.json", "**/.*", "**/node_modules/**"],
25+
"headers": [
26+
{
27+
"source": "**/*.@(woff2|webp|jpg)",
28+
"headers": [
29+
{
30+
"key": "Cache-Control",
31+
"value": "max-age=31556952"
32+
}
33+
]
34+
},
35+
{
36+
"source": "**/*.@(css|js)",
37+
"headers": [
38+
{
39+
"key": "Cache-Control",
40+
"value": "max-age=604800"
41+
}
42+
]
43+
}
44+
]
45+
},
46+
"storage": {
47+
"rules": "_firebase/storage.rules"
48+
},
49+
"emulators": {
50+
"auth": {
51+
"port": 9099
52+
},
53+
"functions": {
54+
"port": 5001
55+
},
56+
"firestore": {
57+
"port": 8080
58+
},
59+
"hosting": {
60+
"port": 5080
61+
},
62+
"pubsub": {
63+
"port": 8085
64+
},
65+
"storage": {
66+
"port": 9199
67+
},
68+
"ui": {
69+
"enabled": true,
70+
"port": 10001
71+
},
72+
"singleProjectMode": true
73+
},
74+
"remoteconfig": {
75+
"template": "_firebase/remoteconfig.template.json"
76+
}
77+
}

0 commit comments

Comments
 (0)