Someone asked to use the DTI API for a small client-side project, so I'm making this change to support it!
As explained in the comment, I think this should be safe regarding CSRF attacks. But it _does_ increase the risk that someday we change something elsewhere that creates a problem, like using cookies to authorize something. So, let's remember to be careful! (as I would hope we would be when adding another auth mechanism!)
Gonna have the /outfit-urls page start returning these instead, for feature parity with before
I might change the strategy on this at some point, like have it get `updatedAt` and redirect instead of generating the image. But this is simpler for now (and the Vercel cache doesn't seem to be as aggressive as I want anyway), and I can change it later!
Oops, we get a _lot_ of outfit image requests, and it's pushing the limits of our free Honeycomb plan! But I don't really need all that much detail, because there's so many.
So, we here apply sampling! `api/outfitImage` is getting a 1/10 rate, and for GraphQL, `ApiOutfitImage` is getting 1/10, and `SearchPanel` is getting 1/5.
I had to add a `addTraceContext` call, to give all the child events awareness of what operation they're being called in, too!
I haven't actually tested that this is working-working, just that the endpoints still return good data. We'll see how it shakes out in prod!
But I did add `console.log(sampleRate, shouldSample, data);` to the `samplerHook` briefly, to see the data flow through, and I reloaded a `SearchPanel` request a few times and observed a plausibly 20% success rate.
Oops, right, I meant to use the new `impress-outfit-images.openneo.net` host for this! It works just fine from `impress-2020.openneo.net` as the backing source right now, but I want these semi-permanent URLs to be a bit more decoupled.
This is mostly for decoupling's sake: I want pretty outfit URLs that aren't dependent on the `/api/outfitImage` path structure, so that we can serve them as ~permanent pretty URLs that don't actually indicate where or how the image is stored.
This reverts commit a0121f09ea.
I didn't realize that VERCEL_URL wouldn't use the canonical URL. And thinking further about the Open Graph semantics, I think having just one canonical host makes more sense, even if it makes testing a bit more annoying in some cases.
Ah right, this is how we get the deployed API code to know its own hostname! This way, when we do a preview deploy, /api/outfitImage will use the preview version of the GraphQL endpoint, too.
Hmm, the built copy of the HTML isn't deployed with the API function on Vercel. Ok! Let's just do the same trick as we did for the dev server, and make an HTTP request.
This is fine enough for perf because we're caching this result locally to the function, plus it should be a fast CDN-cached response anyway.
Size upgrade, wowie! (I figure sharing services probably prefer a higher-resolution image, they're not very big, and to then scale them down for the application-specific use case & device.)
Meta tags are a bit tricky in apps built with `create-react-app`! While some bots like Google are able to render the full page when crawling, not all bots are. Most will just see the empty-ish index.html that would normally load up the application.
But we want outfit sharing to work! And be cool! And use our new outfit thumbnails!
In this change, we add a new server-side rendering API route to handle `/outfits/:id`.
It's very weak server-side rendering: it just loads index.html, and makes a few small tweaks inside the `<head>` tag. But it should be enough for sharing to work in clients that support the basics of Open Graph, which I think most major providers respect! (I know Twitter has their own tags, but it also respects the basics of OG, so let's see whether there's anything we end up _wanting_ to tweak or not!)
Not using this anywhere in-app yet! But might swap it into the user outfits page, and use it to server-side-render social sharing meta tags!
Also eyeing this as a way to replace our nearly 1TB of outfit image S3 storage, and save $20/mo…
Oops, I forgot to grep outside `src` for this, lol! I'll keep the S3 domain support for now, because it's still fine to accept and some clients might be on old code, whatever!
Idk I was surprised to notice that /api/validPetPoses wasn't cached by the Vercel CDN… but I guess that makes sense, because it's only allowed to be an hour old, and client caches hold onto it, so I suppose it makes sense for the CDN cache to not bother.
But in practice, that means users loading the site are pretty _likely_ to see /api/validPetPoses taking its full load time, which can be up to 500ms.
So, I'm offering this additional cache hint, to be willing to serve /api/validPetPose responses up to a week old while reloading the latest data. My hope is that the cache algorithm is much more excited about holding onto that, and that it makes the 500ms delay very _rare_ in practice!
I'm learning that top-level traces should use operation_name as well as name, because name is the low-level thing that every trace gets (including child traces like db queries and net requests)!
Also there was an incorrect label in one of these, and validPetPoses was missing it altogether
This will help us disambiguate items like Butterfly Dress with a non-unique name, and also some where the real item name is glitchy so using it as a key is not wise lol
By printing out this logging data, of item names we have that Waka doesn't, vs item names Waka has that we don't, I was able to solve a lot of them with new code in `normalizeItemName`!
Here's the mismatches that are left at time of writing:
```
[Item: Y, Waka: N] No Waka value for NC DTI item "goldenlulumedallion" (43034)
[Item: Y, Waka: N] No Waka value for NC DTI item "faelliebirthdaybagsurprise" (51350)
[Item: Y, Waka: N] No Waka value for NC DTI item "neopets11thbirthdaycommemorativemysterycapsule" (53448)
[Item: Y, Waka: N] No Waka value for NC DTI item "dyeworksblue:iscawig-blue" (70896)
[Item: Y, Waka: N] No Waka value for NC DTI item "mysteriousdoorwithlocks" (75601)
[Item: Y, Waka: N] No Waka value for NC DTI item "grapefruitnecklace" (77779)
[Item: Y, Waka: N] No Waka value for NC DTI item "discofeverbackground" (79250)
[Item: Y, Waka: N] No Waka value for NC DTI item "featherflaredshoes" (80047)
[Item: Y, Waka: N] No Waka value for NC DTI item "dyeworksredradioactivemutantmarkings" (80441)
[Item: N, Waka: Y] No NC DTI data for Waka item "7thbirthdaycakeslice#1"
[Item: N, Waka: Y] No NC DTI data for Waka item "7thbirthdaycakeslice#2"
[Item: N, Waka: Y] No NC DTI data for Waka item "7thbirthdaycakeslice#3"
[Item: N, Waka: Y] No NC DTI data for Waka item "8thbirthdayrainbowcupcake"
[Item: N, Waka: Y] No NC DTI data for Waka item "8thbirthdaysparklercupcake"
[Item: N, Waka: Y] No NC DTI data for Waka item "8thbirthdaytiedwithabowcupcake"
[Item: N, Waka: Y] No NC DTI data for Waka item "babyspringdress"
[Item: N, Waka: Y] No NC DTI data for Waka item "butterflydress(fromfaeriefestivalevent)"
[Item: N, Waka: Y] No NC DTI data for Waka item "discofever"
[Item: N, Waka: Y] No NC DTI data for Waka item "dyeworksblue:iscawig"
[Item: N, Waka: Y] No NC DTI data for Waka item "dyeworksred:radioactivemutantmarkings"
[Item: N, Waka: Y] No NC DTI data for Waka item "featherflairedshoes"
[Item: N, Waka: Y] No NC DTI data for Waka item "festivebooktree"
[Item: N, Waka: Y] No NC DTI data for Waka item "floralblackcardigan"
[Item: N, Waka: Y] No NC DTI data for Waka item "grapefruitneckace"
[Item: N, Waka: Y] No NC DTI data for Waka item "mysteriousdoorwithlocksbackground"
[Item: N, Waka: Y] No NC DTI data for Waka item "valiantchampionwings"
[Item: N, Waka: Y] No NC DTI data for Waka item "waxcrayonwig"
[Item: N, Waka: Y] No NC DTI data for Waka item "youngsophiesdress"
```
Oops, I missed something important about the Sheets API here! This was causing us to set `{value: undefined}` for some items, which serialized as `{}`.
Just a little experiment to see this working in prod! I don't have permission to actually use this data yet, or a UI for it, just mostly want to see it hooked up to the Sheets API correctly and check the prod cache behavior.
New items have ?v= in the query string, which denied requesting them from the outfit thumbnail API route. Now, we allow any query string.
Also, I started allowing PNGs. I don't think any should actually come through here in practice? But it seems safe and forward-looking.
Huh, I guess TNT changed the folder name from `object` to `items` for new SVGs? Like, some of the old SVGs still are in `objects`, but…
Hmm, I wonder if I should re-run old manifests at some point? I wonder if maybe like, _everything_ changed, and we're just holding onto old URLs now…
When we decided to start out with /api/assetProxy, we didn't know how much the load would be in practice, so we just went ahead and tried it! Turns out, it was too high, and Vercel shut down our deployment 😅
Now, we've off-loaded this to a Fastly CDN proxy, which should run even faster and more efficiently, without adding pressure to Vercel servers and pushing our usage numbers! And I suspect we're gonna stay comfortably in Fastly's free tier :) but we'll see!
(Though, as always, if Neopets can finally upgrade their own stuff to HTTPS, we'll get to tear down this whole proxy altogether!)
Going through matchu@openneo.net was hitting a spam filter on that email host's redirect… Gmail seems to be more okay with it, so I'm sending straight there, and crossing my fingers 🤞
We got bit by the "can't run anything after the response finishes" thing
so I'm just forcing the response to wait for Honeycomb submit to finish
I hope this isn't like, just awful for perf lol. but puts to honeycomb seem fast?
I figure we can get even better messages than "Access denied" in case this happens again!
Also, I need a new deployment to trigger the new env vars, so why not make it count? :)
My hypothesis is that this missing `await` is causing prod Vercel to cut off the request before the async upload finishes. Omitting this `await` was a mistake, so I'm just adding it right in!
I also add logging for the success case. That way, if this isn't actually a fix, I can at least watch the logs and confirm whether the app thinks it's getting to here.