feat(cms): integrate TinaCMS for blog and ecosystem editing#69
feat(cms): integrate TinaCMS for blog and ecosystem editing#69adamsoffer wants to merge 1 commit into
Conversation
- Adds a Git-backed admin UI at /admin for editing content/blog/*.md and content/ecosystem/*.md. Schema covers the full frontmatter for blog posts (title, description, date, author, category, tags, image, imageAlt, draft, body) and ecosystem apps (name, url, description, categories, logo, order, madeBy, links, body). - Image uploads route per-field: blog hero images → public/images/blog/, ecosystem logos → public/ecosystem/. Media manager browses both. - Tina Cloud search enabled via NEXT_PUBLIC_TINA_SEARCH_TOKEN. - tina/tina-lock.json committed (required for Tina Cloud to index branches). - /admin rewrite in next.config.ts so /admin and /admin/index.html both serve the SPA. - requireEnv helper gates Node-only throws to avoid crashing the bundled admin SPA in the browser, where non-NEXT_PUBLIC_ env vars are absent. - pnpm.onlyBuiltDependencies allows native modules (better-sqlite3 for Tina's search index, esbuild, sharp). - lib/ecosystem.ts normalizes logo paths so both legacy filename-only and Tina-written full-path values render correctly. Rolled up from #65, #66, #67, #68. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
📝 WalkthroughWalkthroughThis PR integrates TinaCMS as a headless CMS for managing blog posts and ecosystem app content, adds TinaCMS environment variables and build scripts, updates build infrastructure to handle generated artifacts, and normalizes ecosystem app logo paths to use absolute URLs. ChangesTinaCMS Integration
Build and Development Infrastructure
Ecosystem Logo Path Normalization
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@tina/config.ts`:
- Line 58: The Tina search indexer token is currently read from the
browser-exposed NEXT_PUBLIC_NAMED env var; update the code that sets
search.tina.indexerToken (the indexerToken assignment in tina/config.ts) to read
from the server-only env var process.env.TINA_SEARCH_TOKEN instead of
process.env.NEXT_PUBLIC_TINA_SEARCH_TOKEN, and update the project .env.example
to remove NEXT_PUBLIC_TINA_SEARCH_TOKEN and add TINA_SEARCH_TOKEN so the secret
is not exposed to the client; ensure tina/config.ts remains used only on the
server or in server-side builds.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 6e0e175e-47f7-4745-893f-ebc43e1619c0
⛔ Files ignored due to path filters (7)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yamltina/__generated__/_graphql.jsonis excluded by!**/__generated__/**tina/__generated__/_lookup.jsonis excluded by!**/__generated__/**tina/__generated__/_schema.jsonis excluded by!**/__generated__/**tina/__generated__/frags.gqlis excluded by!**/__generated__/**tina/__generated__/queries.gqlis excluded by!**/__generated__/**tina/__generated__/schema.gqlis excluded by!**/__generated__/**
📒 Files selected for processing (25)
.env.example.gitignore.npmrc.prettierignorecomponents/ecosystem/EcosystemDetail.tsxcomponents/ecosystem/EcosystemListingClient.tsxcontent/ecosystem/blueclaw.mdcontent/ecosystem/daydream.mdcontent/ecosystem/embody.mdcontent/ecosystem/flipsuite.mdcontent/ecosystem/frameworks.mdcontent/ecosystem/higher.mdcontent/ecosystem/livepeer-studio.mdcontent/ecosystem/nytv.mdcontent/ecosystem/spritz.mdcontent/ecosystem/streamplace.mdcontent/ecosystem/thelotradio.mdcontent/ecosystem/tribesocial.mdcontent/ecosystem/ufo.mdeslint.config.mjslib/ecosystem.tsnext.config.tspackage.jsontina/config.tstina/tina-lock.json
|
|
||
| search: { | ||
| tina: { | ||
| indexerToken: process.env.NEXT_PUBLIC_TINA_SEARCH_TOKEN || "", |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
In TinaCMS config (v3.x), is search.tina.indexerTokenexpected to useprocess.env.TINA_SEARCH_TOKEN(server/build-only) rather than aNEXT_PUBLIC_* variable?
💡 Result:
Yes—TinaCMS v3.x expects search.tina.indexerToken to be provided from process.env.TINA_SEARCH_TOKEN (i.e., a server/build-only env var), not from a NEXT_PUBLIC_* variable. Tina’s official config examples wire the search token like this: search: { tina: { indexerToken: process.env.TINA_SEARCH_TOKEN } } [1][2]. The TinaCloud docs also explicitly classify TINA_SEARCH_TOKEN as “Site build only” and “Token for TinaCloud search indexing” [1]. Additionally, Tina’s TinaCloud dashboard documentation warns that “API tokens should be considered sensitive and not be committed” and instructs using environment variables for these tokens in your Tina configuration [3]. So if your tina/config.(ts|js|tsx|jsx) is using TinaCMS v3.x, you should keep TINA_SEARCH_TOKEN non-public and avoid NEXT_PUBLIC_TINA_SEARCH_TOKEN for search.tina.indexerToken.
Citations:
- 1: https://tina.io/docs/reference/config
- 2: https://tina.io/zh/docs/reference/config
- 3: https://tina.io/docs/tinacloud/dashboard/projects
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Locate tina/config.ts and show around the referenced line
if [ -f tina/config.ts ]; then
echo "== tina/config.ts (around line 45-80) =="
nl -ba tina/config.ts | sed -n '45,80p'
else
echo "tina/config.ts not found at repo root"
fi
# Search for env var names across repo (keep output small)
echo "== Search for NEXT_PUBLIC_TINA_SEARCH_TOKEN =="
rg -n "NEXT_PUBLIC_TINA_SEARCH_TOKEN" . || true
echo "== Search for TINA_SEARCH_TOKEN =="
rg -n "TINA_SEARCH_TOKEN" . || true
# Find an env example file and show any relevant lines
echo "== Candidate env example files =="
ls -1 .env* 2>/dev/null || true
for f in .env.example .env.example.* .env*example* 2>/dev/null; do
if [ -f "$f" ]; then
echo "== $f (matching token lines) =="
nl -ba "$f" | rg -n "TINA_SEARCH_TOKEN|NEXT_PUBLIC_TINA_SEARCH_TOKEN" || true
fi
doneRepository: livepeer/website
Length of output: 162
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== Locate tina/config.ts =="
fd -t f "tina/config.ts" . || true
fd -t f "config.ts" . | rg -n "tina/config\.ts$" || true
# Determine actual file path(s)
FILES="$(fd -t f "tina/config.ts" . || true)"
if [ -z "${FILES}" ]; then
echo "No exact tina/config.ts found via fd; attempting broader search for 'indexerToken'..."
FILES="$(fd -t f "config.ts" . | xargs -r rg -l "indexerToken" || true)"
fi
echo "== Candidate files containing indexerToken =="
echo "$FILES"
# For each candidate file, print around first occurrence of NEXT_PUBLIC_TINA_SEARCH_TOKEN or indexerToken
for f in $FILES; do
if [ -f "$f" ]; then
echo "== File: $f =="
echo "NEXT_PUBLIC_TINA_SEARCH_TOKEN occurrences:"
rg -n "NEXT_PUBLIC_TINA_SEARCH_TOKEN" "$f" || true
echo "TINA_SEARCH_TOKEN occurrences:"
rg -n "TINA_SEARCH_TOKEN" "$f" || true
echo "Snippet around 'indexerToken' in $f:"
line="$(rg -n -m1 "indexerToken" "$f" | cut -d: -f1 || true)"
if [ -n "${line}" ]; then
start=$((line-8))
end=$((line+8))
if [ "$start" -lt 1 ]; then start=1; fi
sed -n "${start},${end}p" "$f"
fi
fi
done
echo "== Repo-wide env var name search =="
echo "NEXT_PUBLIC_TINA_SEARCH_TOKEN:"
rg -n "NEXT_PUBLIC_TINA_SEARCH_TOKEN" . || true
echo "TINA_SEARCH_TOKEN:"
rg -n "TINA_SEARCH_TOKEN" . || true
echo "== Candidate env example files =="
ls -1 .env* 2>/dev/null || true
for f in .env.example .env.example.* .env*example* 2>/dev/null; do
[ -f "$f" ] || continue
echo "== $f (token lines) =="
rg -n "NEXT_PUBLIC_TINA_SEARCH_TOKEN|TINA_SEARCH_TOKEN" "$f" || true
doneRepository: livepeer/website
Length of output: 1323
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== .env.example token occurrences =="
if [ -f .env.example ]; then
rg -n "NEXT_PUBLIC_TINA_SEARCH_TOKEN|TINA_SEARCH_TOKEN" .env.example || true
echo "== .env.example surrounding lines (if token present) =="
# Print a small window around each match
for v in NEXT_PUBLIC_TINA_SEARCH_TOKEN TINA_SEARCH_TOKEN; do
line="$(rg -n -m1 "$v" .env.example | cut -d: -f1 || true)"
if [ -n "$line" ]; then
start=$((line-5)); end=$((line+5))
[ "$start" -lt 1 ] && start=1
sed -n "${start},${end}p" .env.example
fi
done
else
echo ".env.example not found"
fi
echo "== tina package version (if available) =="
if [ -f package.json ]; then
cat package.json | rg -n "\"tina\"" || true
# More robust: search for "tinacms" too
cat package.json | rg -n "tinacms|tina" || true
fi
echo "== Any other NEXT_PUBLIC_TINA_SEARCH_TOKEN usage in repo =="
rg -n "NEXT_PUBLIC_TINA_SEARCH_TOKEN" . || trueRepository: livepeer/website
Length of output: 1095
Keep the Tina search indexer token server-only.
tina/config.ts reads search.tina.indexerToken from process.env.NEXT_PUBLIC_TINA_SEARCH_TOKEN, and .env.example defines NEXT_PUBLIC_TINA_SEARCH_TOKEN. In Next.js, NEXT_PUBLIC_* values are exposed to the browser, so this can leak a sensitive search indexing token—TinaCMS v3.x expects process.env.TINA_SEARCH_TOKEN instead.
🔧 Proposed fix
- indexerToken: process.env.NEXT_PUBLIC_TINA_SEARCH_TOKEN || "",
+ indexerToken: process.env.TINA_SEARCH_TOKEN || "",-# Search indexer token (different token type from TINA_TOKEN). Create via
+# Search indexer token (different token type from TINA_TOKEN). Create via
# Tokens → New Token → Search in the Tina dashboard.
-NEXT_PUBLIC_TINA_SEARCH_TOKEN=
+TINA_SEARCH_TOKEN=🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@tina/config.ts` at line 58, The Tina search indexer token is currently read
from the browser-exposed NEXT_PUBLIC_NAMED env var; update the code that sets
search.tina.indexerToken (the indexerToken assignment in tina/config.ts) to read
from the server-only env var process.env.TINA_SEARCH_TOKEN instead of
process.env.NEXT_PUBLIC_TINA_SEARCH_TOKEN, and update the project .env.example
to remove NEXT_PUBLIC_TINA_SEARCH_TOKEN and add TINA_SEARCH_TOKEN so the secret
is not exposed to the client; ensure tina/config.ts remains used only on the
server or in server-side builds.
Summary by CodeRabbit
New Features
/admin.Configuration