Add in app video creation example - #14
Conversation
Companion code for the "Add video creation to your app without building an editor" guide: an embedded Studio SDK editor, a quick form, and a one-click headless render, all through one render proxy that keeps the API key server-side, records render ownership, validates asset URLs on both render paths, and rate limits per user.
Server: re-fetch the render from the API in the webhook receiver instead of trusting the unauthenticated payload's url, widen the private-address check to IPv4-mapped, unique-local and link-local IPv6, match leading whitespace in the asset-URL collector so " https://..." can't skip validation, and cap the request body. Client: register the form, headless and reset handlers without waiting on the editor mount so they work while it loads, seed the gallery from the user's own renders on load, add the missing audio track to the headless edit so all three paths produce the same video, and report a missing render proxy clearly instead of a JSON parse error.
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
dazzatronus
left a comment
There was a problem hiding this comment.
Review — STANDARDS.md must be followed
Public cookbook code is copy-paste for customers. Blocking on STANDARDS.md gaps (and one security teaching hole). Align with examples/_template/ and root Prettier — not Studio SDK formatting.
Blockers (STANDARDS)
.env.exampleincomplete — STANDARDS: empty value + comment with URL above each variable. This example also readsSHOTSTACK_ENV,SHOTSTACK_TEMPLATE_ID,RATE_LIMIT,PORT. Document each (empty) or drop undocumented knobs from the README/server contract.- README vs template / STE — Use the five-section shape from
_template(Related guide:line). Shorten Requirements / intro to STE limits (≤20 words/instruction, ≤25/description). - Asset URL validation + redirects (
server.js) — DNS checks the hostname, thenfetch(..., { redirect: 'follow' })can land on a private address. For an example that teaches URL validation, refuse redirects, re-validate the final URL, or document this next to the DNS-TOCTOU note.
Already good
package.jsonname /engines/formatscript- Key held server-side; ownership; rate-limit honesty in comments
- Second commit webhook re-fetch / IPv6 private ranges / gallery seed / headless audio
Please re-run the STANDARDS “before you make a PR” checklist (no key / wrong key / README sequence / prettier) after fixes.
| @@ -0,0 +1,2 @@ | |||
| # https://dashboard.shotstack.io/register | |||
| SHOTSTACK_API_KEY= | |||
There was a problem hiding this comment.
STANDARDS — API keys: document every env var this example reads (SHOTSTACK_ENV, SHOTSTACK_TEMPLATE_ID, optional RATE_LIMIT / PORT) with an empty value and a comment+URL above each line. A missing key must fail with your one-line message, not a late API error.
|
|
||
| let head; | ||
| try { | ||
| head = await fetch(url, { method: 'HEAD', redirect: 'follow' }); |
There was a problem hiding this comment.
Correctness / teaching: after the DNS private-address check, redirect: "follow" can 302 onto link-local / RFC1918. Harden (no follow, or re-check final URL host) or spell this out beside the DNS-TOCTOU comment — STANDARDS expects examples to be safe to copy.
| proxy. The proxy holds the API key, records which user owns each render, validates user-supplied | ||
| asset URLs, and rate limits each user. | ||
|
|
||
| Companion code for [Add video creation to your app without building an editor](https://shotstack.io/learn/add-video-creation-to-your-app/). |
There was a problem hiding this comment.
STANDARDS — README: match examples/_template (Related guide: …) and STE (short sentences, active voice). Trim the Requirements sandbox-key sentence — it currently runs long for a description.
dazzatronus
left a comment
There was a problem hiding this comment.
Follow-up from a STANDARDS / public-polish pass (beyond the first review):
- Pin Studio SDK exactly —
package.jsonhas^2.15.1while the lockfile resolves 2.18.0. Public demos should pin an exact version (and keep lockfile in sync) so fresh clones don’t silently drift. - Timeline CSS — on
[data-shotstack-timeline], SDK expects a fixed height andoverflow: hidden. Addoverflow: hiddenso the timeline doesn’t paint outside the box. - Outbound fetch timeouts — add
AbortSignal.timeout(...)(or equivalent) on Shotstack/HEADfetches so a hung network fails with one line instead of hanging the proxy (seeexamples/first-render).
Still blocked on the earlier STANDARDS items (.env.example, README/STE, redirect-after-DNS).
| "node": ">=20" | ||
| }, | ||
| "dependencies": { | ||
| "@shotstack/shotstack-studio": "^2.15.1" |
There was a problem hiding this comment.
Pin an exact @shotstack/shotstack-studio version (lockfile currently resolves 2.18.0). Caret ranges make public cookbook clones non-reproducible.
.env.example documents every variable the proxy reads (SHOTSTACK_ENV, SHOTSTACK_TEMPLATE_ID, RATE_LIMIT, PORT) with an empty value and a comment above each. server.js uses || so an empty value in .env falls back to the default, and a bad PORT, RATE_LIMIT or SHOTSTACK_ENV exits with one line. README follows the _template shape (Related guide line, five sections) and STE sentence limits, and says the x-demo-user header is a stand-in for auth. Asset URL validation refuses redirects: the HEAD check uses redirect manual and rejects any 3xx, so a public host cannot 302 onto a private address after the DNS check. The comment beside the DNS note explains the alternative. Studio SDK pinned to 2.18.0 to match the lockfile. The timeline container has overflow hidden. Every Shotstack fetch goes through an api() helper with a 30 s AbortSignal.timeout, the HEAD check has 10 s, and timeouts and network failures return one actionable line instead of hanging the proxy.
Companion code for Add video creation to your app without building an editor (https://shotstack.io/learn/add-video-creation-to-your-app/): an embedded Studio SDK editor, a quick form, and a one-click headless render, all through one render proxy that keeps the API key server-side, records render ownership, validates asset URLs on both render paths, and rate limits per user.
Gated live on the sandbox: all three paths render to done (the editor verified mounting in headless Chrome), missing and wrong keys fail with one line and a non-zero exit, non-HTTPS and private-address asset URLs are rejected on both render paths, and the ownership record keeps one user's renders invisible to another. Second commit addresses code review: the webhook receiver re-fetches the render from the API instead of trusting the unauthenticated payload, the private-address check covers IPv4-mapped and link-local IPv6, the gallery seeds from the user's own renders on load, and the headless path gained the audio track the other two paths already had. Prettier passes with the root config.