-
-
Notifications
You must be signed in to change notification settings - Fork 189
feat(api): switch to unkey for rate limiting #292
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -17,6 +17,7 @@ export const env = createEnv({ | |
| VERCEL_URL: z.string().optional(), | ||
| UPSTASH_REDIS_REST_URL: z.string().url(), | ||
| UPSTASH_REDIS_REST_TOKEN: z.string().min(1), | ||
| UNKEY_ROOT_KEY: z.string().min(1), | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 Verification agent 🧩 Analysis chainUNKEY_ROOT_KEY is required — confirm usage stays server-only and available in non-prod
If local dev without Unkey is common, consider a no-op limiter when 🏁 Script executed: #!/bin/bash
# Ensure all env imports use the server entry to prevent leaking secrets
rg -nP '@repo/env(?!/server)' -g '!**/node_modules/**' || echo "OK: only server imports detected."Length of output: 827 Fix server-only imports for The environment variable
Optional: If local development without Unkey credentials is expected, consider implementing a no-op or “fail-open” path when 🤖 Prompt for AI Agents |
||
| MARBLE_WORKSPACE_KEY: z.string().min(1).optional(), | ||
| MARBLE_API_URL: z.string().url().optional(), | ||
| }, | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -16,6 +16,7 @@ | |
| "NEXT_PUBLIC_VERCEL_ENV", | ||
| "UPSTASH_REDIS_REST_URL", | ||
| "UPSTASH_REDIS_REST_TOKEN", | ||
| "UNKEY_ROOT_KEY", | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 Verification agent 🧩 Analysis chainUNKEY_ROOT_KEY added to build/dev env — make sure CI and preview envs provide it Good to see
Consider listing Also applies to: 52-52 🏁 Script executed: #!/bin/bash
# Verify references to UNKEY_ROOT_KEY across workflows and docs
fd -H -a '.github/workflows' 2>/dev/null | xargs -r rg -n "UNKEY_ROOT_KEY" || echo "No references in workflows; ensure it's added where needed."
# Double-check no client code is reading UNKEY_ROOT_KEY directly
rg -nP --type=ts --type=tsx -g '!**/node_modules/**' 'process\.env\.UNKEY_ROOT_KEY' || echo "OK: no direct client reads found."Length of output: 251 🏁 Script executed: #!/bin/bash
# Locate any .env, .env.example, or related environment files
fd -t f -g '.env*' 2>/dev/null
# Search for UNKEY_ROOT_KEY in environment files and Markdown docs
rg -n 'UNKEY_ROOT_KEY' -g '*.env*' -g '*.md' || echo "UNKEY_ROOT_KEY not found in env/example or docs"Length of output: 170 🏁 Script executed: #!/bin/bash
echo "Listing all GitHub Actions workflow files:"
fd -t f -g ".github/workflows/*.yml" || echo "No workflow files found"
echo
echo "Checking for .env.example or .env.sample files:"
fd -t f -g ".env.example" -g ".env.sample" || echo "No example env files found"Length of output: 714 🏁 Script executed: #!/bin/bash
set -e
echo "==> Finding GitHub Actions workflow files"
if [ -d .github/workflows ]; then
find .github/workflows -type f \( -name '*.yml' -o -name '*.yaml' \) -print
else
echo "⚠️ No .github/workflows directory found"
fi
echo
echo "==> Searching workflows for UNKEY_ROOT_KEY"
if [ -d .github/workflows ]; then
grep -RHn 'UNKEY_ROOT_KEY' .github/workflows || echo "⚠️ UNKEY_ROOT_KEY not found in any workflow"
fi
echo
echo "==> Checking for .env.example and .env.sample"
for f in .env.example .env.sample; do
if [ -f "$f" ]; then
echo "Found $f — checking contents"
grep -Hn 'UNKEY_ROOT_KEY' "$f" || echo "⚠️ UNKEY_ROOT_KEY missing in $f"
else
echo "ℹ️ $f not present"
fi
doneLength of output: 1107 Ensure UNKEY_ROOT_KEY is configured in CI workflows and example env files Verification shows that although you’ve wired
Please add:
Also confirm that any staging/preview deployment pipelines export the same environment variable. 🤖 Prompt for AI Agents |
||
| "SIMPLE_ANALYTICS_HOSTNAME", | ||
| "NEXT_PUBLIC_SIMPLE_ANALYTICS_HOSTNAME", | ||
| "MICROSOFT_CLIENT_ID", | ||
|
|
@@ -48,6 +49,7 @@ | |
| "NEXT_PUBLIC_VERCEL_ENV", | ||
| "UPSTASH_REDIS_REST_URL", | ||
| "UPSTASH_REDIS_REST_TOKEN", | ||
| "UNKEY_ROOT_KEY", | ||
| "SIMPLE_ANALYTICS_HOSTNAME", | ||
| "NEXT_PUBLIC_SIMPLE_ANALYTICS_HOSTNAME", | ||
| "MICROSOFT_CLIENT_ID", | ||
|
|
||
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.
💡 Verification agent
🧩 Analysis chain
Prevent duplicate waitlist entries and normalize emails
There’s a race between the existence check and the insert, which can yield duplicates under concurrency. Also, email comparisons are typically case-insensitive; normalize before comparing/inserting.
waitlist.emailat the database level.onConflictDoNothing(or equivalent) and normalize email:Consider returning the same generic message regardless of prior presence to avoid leaking list membership.
🏁 Script executed:
Length of output: 246
Add unique constraint on
waitlist.emailand use atomic insert with normalized emailsThe current implementation still risks duplicate waitlist entries under concurrency and treats emails case-sensitively. We’ve verified there’s no unique constraint on
waitlist.emailin your Drizzle schema; please add one and switch to an atomic “insert-or-ignore” flow using a normalized email.• db/schema.ts (Drizzle schema)
– Add
.unique()(or a unique index) to theemailcolumn on thewaitlisttable.• packages/api/src/routers/early-access.ts (mutation resolver)
– Normalize the incoming email via
input.email.trim().toLowerCase().– Replace the separate existence check + insert with:
diff .mutation(async ({ input }) => { - // existing exists-then-insert logic + const email = input.email.trim().toLowerCase(); + + const inserted = await db + .insert(waitlist) + .values({ email }) + .onConflictDoNothing({ target: waitlist.email }); + + if (inserted.rowCount === 0) { + return { message: "You're already on the waitlist!" }; + } + return { message: "You've been added to the waitlist!" }; })• (Optionally) To prevent leaking who’s on the list, unify both outcomes under a generic message like
"Your request has been received."[optional]📝 Committable suggestion
🤖 Prompt for AI Agents