Commit graph

175 commits

Author SHA1 Message Date
8b3c256a5c Time out manifest requests after 2sec
We do a thing where we sometimes proactively update an appearance layer's manifest from images.neopets.com when it's been a while since the last time, _during_ user requests.

But when images.neopets.com is being slow, this makes our API requests about appearances super slow, too!

In this change, we add a 2-second timeout to those requests. That should be plenty for when images.neopets.com is in a good mood, but also give up fast enough for the site to not feel miserable lol :p (especially when the use "Use DTI's image archive" option is on!)
2022-10-13 17:00:21 -07:00
a1844f76e0 Add /api/assetImageRedirect
Okay, this is gonna be a drop-in new backend for impress-asset-images.openneo.net, to enable Classic DTI to use the same images as DTI 2020!

This will enable us to stop generating images and uploading them to S3 just for Classic's sake, so we can turn those background processes off! And the new modeling script skips that anyway, so this is an important compatibility step for the new data that went out today!
2022-10-11 12:21:14 -07:00
29d9d498bf Use aws.impress-asset-images.openneo.net
We're gonna update impress-asset-images.openneo.net to perform redirects and stuff, so Classic DTI can start using the same images that DTI 2020 does.

That should enable us to stop relying on AWS for images, which is important because the new modeling script breaks that anyway :p but this will also let us turn off the image converters that run in the background all the time, and I'm excited for that too!
2022-10-11 12:19:39 -07:00
e8d7f6678d Auto-modeling script??
It seems to be working!! How exciting!! I'm just letting it run on stuff now :3

One important issue is that Classic DTI doesn't show images for items modeled this way, because we don't download the SWFs for it. But I wanna update it to stop using AWS anyway and do the same stuff 2020 does, I think we can do that pretty sneakily!
2022-10-11 11:13:10 -07:00
052cc242e4 Modeling page performance fix
Ok so, I kinda assumed that the query engine would only compute `all_species_ids_for_this_color` on the rows we actually returned, and it's a fast subquery so it's fine. But that was wrong! I think the query engine computing that for _every_ item, and _then_ filter out stuff with `HAVING`. Which makes sense, because the `HAVING` clause references it, so computing it makes sense!

In this change, we inline the subquery, so it only gets called if the other conditions in the `HAVING` clause don't fail first. That way, it only gets run when needed, and the query runs like 2x faster (~30sec instead of ~60sec), which gets us back inside some timeouts that were triggering around 1 minute and making the page fail.

However, this meant we no longer return `all_species_ids_for_this_color`, which we actually use to determine which species are _left_ to model for! So now, we have a loader that also basically runs the same query as that condition subquery.

A reasonable question would be, at this point, is the `HAVING` clause a good idea? would it be simpler to do the filtering in JS?

and I think it might be simpler, but I would guess noticeably worse performance, because I think we really do filter out a _lot_ of results with that `HAVING` clause—like basically all items, right? So to filter on the JS side, we'd be transferring data for all items over the wire, which… like, that's not even the worst dealbreaker, but it would certainly be noticed. This hypothesis could be wrong, but it's enough of a reason for me to not bother pursuring the refactor!
2022-10-10 20:15:16 -07:00
773ec8974f petOnNeopetsDotCom GQL improvements
We now support returning `null` from `petAppearance` when a pet genuinely has no customization data.

We also deprecate some old fields, and update our own call site to match.
2022-09-17 22:07:34 -07:00
50e04385e3 Account creation is ready!
Yay it seems to be working, owo! Added the actual database work, and some additional uniqueness checking.
2022-09-13 21:11:48 -07:00
08603af961 Create account endpoint skeleton + validation
It doesn't actually create the account, but it does some field validation and the form reacts to it!
2022-09-12 15:25:22 -07:00
c7ba61a0f1 Track sign-ins & IP addresses
Oh right, these are some logging-ish things that Classic DTI would perform! It's easy enough for us to keep the fields up-to-date too, so let's do it!
2022-09-12 15:24:58 -07:00
4c9dbf91fb Use latest ~owls NC trade values API
They're moving away from the bulk endpoint to individual item data lookups, so we're updating to match!
2022-09-04 01:35:05 -07:00
9982cbf93d Refactor login/logout mutations a smidge
I decided that `setAuthToken` was the more appropriate server-level API, and that letting the login/logout mutations engage with the auth library made more sense to me!
2022-08-26 16:05:11 -07:00
db8fe9f3c2 Logout button for new auth mode
Hey hey, logging out works! The server side of this was easy, but I made a few refactors to support it well on the client, like `useLoginActions` becoming just `useLogout` lol, and updating how the nav menu chooses between buttons vs menu because I wanted `<LogoutButton />` to contain some state.

We also did good Apollo cache stuff to update the page after you log in or out! I think some fields that don't derive from `User`, like `Item.currentUserOwnsThis`, are gonna fail to update until you reload the page but like that's fine idk :p

There's a known bug where logging out on the Your Outfits page turns into an infinite loop situation, because it's trying to do Auth0 stuff but the login keeps failing to have any effect because we're in db mode! I'll fix that next.
2022-08-17 16:05:36 -07:00
4d0c48ab7c Login form checks the db, and saves a cookie
Okay so one of the trickiest parts of login is done! 🤞 and now we need to make it actually show up in the UI. (and also pressure-test the security a bit, I've only really checked the happy path!)
2022-08-17 00:58:52 -07:00
38957c50c0 Merge branch 'owls-integration' into main 2022-08-16 00:16:09 -07:00
240a683e71 Finish wiring up ~owls data
Hey nice it looks like it's working! :3 "Bright Speckled Parasol" is a nice test case, it has a long text string! And when the NC value is not included in the ~owls list, we indeed don't show the badge!
2022-08-15 23:57:33 -07:00
25092b865a Add Delete button for outfits
Hey finally! I got in the mood and did it, after… a year? idk lol

The button should only appear for outfits that are already saved, that are owned by you. And the server enforces it!

I also added a new util function to give actually useful error messages when the GraphQL server throws an error. Might be wise to use this in more places where we're currently just using `error.message`!
2022-08-15 20:23:17 -07:00
5dfd67a221 Oops, fix a security error in outfit saving
Uhhh I guess I never added the check that the outfit you're editing is your own? Embarrassing.

I don't have any reason to believe anyone abused this, but 😬! Good to have fixed now!
2022-08-15 19:51:31 -07:00
b9ba650992 Use HTTPS for AMFPHP requests
This was a bit trickier to figure out how to upgrade, it's not in the `xmlrpc` package's README, but I found the answer here: https://github.com/baalexander/node-xmlrpc/issues/142
2022-08-03 14:40:00 -07:00
bd9017796e Use HTTPS for images.neopets.com and pets.neopets.com
Tbh I didn't even really validate these changes, or that the codepaths right now aren't working, they just seem like clear drop-in upgrades now that HTTPS works and HTTP requests are redirected. Simplify!
2022-08-03 14:36:07 -07:00
3744a476e5 Fix db transactions with pooling
Ah okay, pools support `query` and `execute` the same way connection objects do (as a shorthand for acquiring, querying, and releasing), but it doesn't have the same helpers for transactions. Makes sense: you need those queries to go to the same connection, and an API where you just call it against the pool object can't tell that it's part of the same thing!

Now, we have our transaction code explicitly acquire a connection to use for the duration of the transaction.

An alternative considered would have been to have `connectToDb` acquire a connection, and then release it at the end of the GraphQL request. That would have made app code simpler, but added a lot of additional potential surprise failure points to the infra imo (e.g. what if we're misunderstanding the GraphQL codepath and the connection never gets released? whereas here it's relatively easy to audit that there's a `finally` in the right spot.)
2022-01-08 18:49:00 -08:00
e051290df4 Better-scoped queries for currentUserOwnsThis etc
I hypothesize that loading people's full trade lists more often than necessary is part of the cause of the recent mega slowdown!

My hypothesis is that we're clogging up the MySQL connection socket with a ton of data, which blocks all other queries until the big ones come through and parse out. (I haven't actually validated my assumption that MySQL connections send query results in serial like that, but it makes sense to me, and fits what I've been seeing.)

There's more places we could potentially optimize, like the trade list page itself… (we currently aggressively load everything when we could limit it and load the rest on the followup pages, or even paginate the followup pages…)

…but my hope is that this helps enough, by relieving the load on the homepage (latest items) and on item searches!
2022-01-07 11:37:27 -08:00
e95f6abbe4 Closet list dropdowns on item page
Oooh this feature is feeling very nice :) :) We hid "not in a list" pretty smoothly I think!

A known bug: If you have the item in a list, then click the big colorful button, it will remove the item from *all* lists; and then if you click it again, it will add it to Not in a List. But! The UI will still show the lists it was in before, because we haven't updated the client cache. (It's not that bad in the middle state though, because the list dropdown stuff gets hidden.)
2021-11-30 16:36:00 -08:00
ad249d03ab WIP list editing on item page
Gonna have this button pop up a little thing of your lists, with checkboxes, and I guess probably quantities once I add that concept back to 2020 😅
2021-11-26 14:49:21 -08:00
2e41f7bb0b Send Vary: Authorization cache header
I don't think this is actually relevant in-app right now, but I figured sending it is More Correct, and is likely to prevent future bugs if anything (and prevent future question about why we're _not_ sending it).

I also removed the `maxAge: 0` on `currentUser`, now that I've updated Fastly to no longer default to 5-minute caching when no cache time is specified. I can see why that's a reasonable default for Fastly, but we've been pretty careful about specifying Cache-Control headers when relevant, so the extra caching is mostly incorrect.
2021-11-23 13:00:56 -08:00
b941dce9fa Private cache headers in item search
If the user is searching for things they own or want, make sure we don't CDN cache it!

For many queries, this is taken care of in practice, because the search result includes `currentUserOwnsThis` and `currentUserWantsThis`. But I noticed in testing that, if the search result has no items, so those fields aren't actually part of the _response_, then the private header doesn't get set. So this mainly makes sure we don't accidentally cache an empty result from a user who didn't have anything they owned/wanted yet!
2021-11-16 13:09:45 -08:00
cadf7487af Mark currentUser GQL as non-cacheable
Comments explain most of this! Vercel changed around the Cache-Control headers a bit to always essentially apply max-age:0 when scope:PRIVATE was true.

I'm noticing this isn't *fully* working yet though, because we're not getting a `Cache-Control: private` header, we're just getting no header at all. Fastly might aggressively choose to cache it anyway with etag stuff! I bet that's the fault of our caching middleware plugin thing, so I'll check on that!
2021-11-16 12:12:51 -08:00
002af474f8 Don't HTTP cache currentUserOwnsThis/wants
Hmm, I see, Vercel chews on Cache-Control headers a bit more than I'm used to, so anything marked `scope: PRIVATE` would not be cached at all.

But on a more standard server, this was coming out as privately cacheable, but for an actual amount of time (1 hour in the homepage case), because of the `maxAge` on other fields. That meant the device browser cache would hold onto the result, and not always reflect Own/Want changes upon page reload.

In this change, we set `maxAge: 0`, because we want this field to be very responsive. I also left `scope: PRIVATE`, even though I think it doesn't really matter if we're saying the field isn't cacheable anyway, because I want to set the precendent that `currentUser` fields need it, to avoid a potential gotcha if someone creates a cacheable `currentUser` field in the future. (That's important to be careful with though, because is it even okay for logouts to not clear it? TODO: Can we clear the private HTTP cache somehow? I guess we would need to include the current user ID in the URL?)
2021-11-16 12:04:16 -08:00
0a81f07849 Remove Waka values
The motivation is that I want VERCEL_URL and local net requests outta here :p and we were doing some cutesiness with leveraging the CDN cache to back the GQL fields. No more of that, folks! lol
2021-11-12 22:06:50 -08:00
299561d1e3 Paginate the user outfits page
My main inspiration for doing this is actually our potentially-huge upcoming Vercel bill lol

From inspecting my Honeycomb dashboard, it looks like the main offender for backend CPU time usage is outfit images. And it looks like they come in big spikes, of lots of low usage and then suddenly 1,000 requests in one minute.

My suspicion is that this is from users with many saved outfits loading their outfit page, which previously would show all of them at once.

We do have `loading="lazy"` set, but not all browsers support that yet, and I've had trouble pinning down the exact behavior anyway!

Anyway, paginating makes for a better experience for those huge-list users anyway. We've been meaning to do it, so here we go!

My hope is that this drastically decreases backend CPU hours immediately 🤞 If not, we'll need to investigate in more detail where these outfit image requests are actually coming from!

Note that I added the pagination to the existing `outfits` GraphQL endpoint, rather than creating a new one. I felt comfortable doing this because it requires login anyway, so I'm confident that other clients aren't using it; and because, while this kind of thing often creates a risk of problems with frontend and backend code getting out of sync, I think someone running old frontend code will just see only their first 30 outfits (but no pagination toolbar), and get confused and refresh the page, at which point they'll see all of them. (And I actually _prefer_ that slightly confusing UX, to avoid getting more giant spikes of outfit image requests, lol :p)
2021-11-01 19:33:40 -07:00
2fb84b0b0f Better handle editUsername when Auth0 update fails
Hmm, right, okay, we *generally* should have all users imported to Auth0, but this can fail if the cron job is behind or Auth0 rejected the data (e.g. user data in a format it doesn't support).

Previously, this would apply the name change in the database, but return Auth0's "The user does not exist." error to the GraphQL client, making it look like the update fully failed.

In this change, we handle that case differently: when the Auth0 update fails with a 404, we proceed but log a warning; and when Auth0 fails with an unexpected error, we roll back the database change in addition to raising the error to the client, to keep the behavior obvious and consistent.
2021-10-21 12:17:12 -07:00
6efd542f49 Wire up the Remove button for item lists
Did some stuff in here for parsing the default list ID too. We skipped that when making the new list index page, but now maybe you could reasonably link to the default list? 🤔 not sure it's a huge deal though
2021-09-30 19:26:09 -07:00
f036890aa1 Use /api/assetImage for all image sizes
We update /api/assetImage to accept size as a parameter (I make it mandatory to push people into HTTP caching happy paths), and we update the GraphQL thing to use it in those cases too!

This also means that, if these images seem to go well, we could swap Classic DTI over to them… I want to turn off those RAM-heavy image converters on the VPS lol
2021-08-19 17:56:09 -07:00
7719ab8c07 Use /api/assetImage for imageUrl
In this change, we start using our new API endpoint for movie image URLs, instead of the Classic DTI image.

This should make the little fade-in phase for certain movies a little bit less jarring (the part where we preload the image before the movie loads), though I suppose that won't necessarily load as fast until it gets into the cache the first time lol. (A good reason to maybe put a more long-lived cache like Fastly in front of this stuff long-term?)

Not doing it for the smaller image sizes yet, I'm a bit worried that I don't 100% know how to teach /api/assetImage to resize without tipping over the function limit…

…oh! I should have the webpage render at different sizes! Yeah that's a great idea lol
2021-08-19 17:40:16 -07:00
af493f6719 Add fit info to homepage
I wasn't sure how to fill the space for items that are fully modeled, then realized some basic at-a-glance "who does this fit" would help!

The load time isn't great, I think I need to break out that dependent subquery, but maybe the stale-while-revalidate will cover it well enough at first.
2021-07-11 19:08:47 -07:00
f2259d6487 Add modeling info to homepage
To make this fast, I had to tweak the GraphQL resolver a bit to run a filtered version of the query for `newestItems` instead of scanning the full database! But yeah, looking good!

I think I'm gonna want to swap out "Fully modeled" for some insight about who it fits
2021-07-11 18:09:29 -07:00
b0672d589f Oops, stop showing PNG reference art
Huh, weird. So I reversed the manifest, because you want to get the *last* movie. And I figured that semantic probably extended to PNGs and SVGs too?

But actually, PNGs sometimes have *other* PNGs in the manifest that aren't the relevant asset at all, and are just reference art.

Again, I'm really not sure what the underlying semantic is here? Does the Neopets customizer just display them all, and for the items with this problem, they happen to layer in a way that's not broken?? I would really like to not do that, and I would really like to know the real semantic, but I can't find it >.>

So um, I'm going ahead and using the best semantic that solves the problems I know about? Which is, use the last movie, and use the first PNG. Fingers crossed lol!

I also didn't test this change extensively, because I'm on a train lol

I'm just trusting that this push will be better than what we had before. I tested it on the Dandan MME, which has two JS files, and it took the latter; and the Pathway of Petals Background, which has two PNG files, and it took the former. Success? 😬🤞
2021-06-27 15:34:36 -07:00
35a1096da3 Fix movie bugs with Dandan, Electric Dress, etc
Huh, so it turns out sometimes the manifest will include old broken conversion attempts!

This fixed the "MiniMME18-S2c: Holomorphic Foliage and Dandan Set", the "Electric Dress" on various species (incl. Aisha), and yeah!

What an interesting discovery 😂
2021-06-24 18:54:48 -07:00
c2ef164ff2 Cache item search pages
Ah right okay, when the `ItemSearchResultV2` doesn't have an `id`, Apollo Cache isn't quite so strong about caching conflicting-y fields, like the different parameterizations of `items`.

With this change, we give the search result object an ID, which helps Apollo cache more confidently!

It's just a serialization of the relevant search fields 😅
2021-06-21 13:48:45 -07:00
3537ef9a6f Use itemSearchV2 in wardrobe too
That's the last itemSearch call site! I'll probably keep it up for other clients for a while though, esp since it doesn't depend on any additional loaders or anything, it's pretty small overall

Updated the comments to reflect this, and also remembered to make them real docstrings lol!
2021-06-21 10:37:54 -07:00
c38678cf1a Build itemSearchV2 in GQL
The main change is that we restructure the query, so that only the parts that are actually affected by pagination depend on those variables!

This will enable the Apollo Cache to trivially cache and show `numTotalItems` while waiting for other pages to load.
2021-06-21 10:30:41 -07:00
3cd0ffd764 Fix private lists page
Oops, I pulled `currentUserId` from the wrong place, so it was always acting as if you're logged in! Now, you can see the list page for your own private list!
2021-06-18 17:25:05 -07:00
5c5bdb11ff Oops, fix Lists page links!
Right, oops, the redirect works when you navigate directly to a URL, but not client-side! Fixed a bunch of 'em 😅
2021-06-15 22:46:43 -07:00
cf30b25be0 First draft of UserItemListPage
A lot is missing! No descriptions, no support for the "Not in a list" case, no scroll performance windowing, no editing!

But it's a start :3
2021-06-12 04:45:23 -07:00
39f6c6f4ac Explain why AppearanceLayer.imageUrl can be null 2021-06-02 14:27:11 -07:00
d461686bc3 Fix Maraquan modeling
This was a known oversight, that I've finally fixed because I realized this subquery probably would be just fine lol!

Now, instead of removing rows with _all_ species modeled, we remove rows with all species _for that color_ modeled.

This leaves the rest of the modeling list unchanged, but removed 10 Maraquan items that were done modeling but still on the list:
- Dyeworks Coral: Maraquan White Beaded Gown
- Dyeworks Green: Maraquan White Beaded Gown
- Dyeworks Lavender: Maraquan White Beaded Gown
- Dyeworks Purple: Maraquan Wig with Negg Accessory
- Dyeworks Lavender: Maraquan Sea Blue Gown
- Dyeworks Pink: Maraquan Sea Blue Gown
- Dyeworks Silver: Maraquan Sea Blue Gown
- Maraquan White Lace Gown
- Underwater Maraquan Markings

(I also went in the database and marked the "Maraquan Ocean Blue Contacts" with the `modeling_status_hint = "done"`, because it's not compatible with Lutari.)
2021-05-27 17:39:56 -07:00
8525ac393f Fix GraphQL docstrings
Oops, right, I forgot for a while that GraphQL fields have a special syntax for docstrings, and it's not just comments! This will help stuff show up in our GraphQL Playground API docs correctly 🥰
2021-05-27 16:51:31 -07:00
abc322c24d Remove imageUrl from Outfit GQL
I'm not sure which image url is better to return from stuff like this, and I don't actually have a use case for it anymore, so let's just clear it out until we need something like it!
2021-05-26 20:01:03 -07:00
f9f8cdc553 Wire up the bulk outfit image converter
Heck yeah, it's looking great!! Good error handling too :) you can test the partial error case by changing some image URLs to have invalid IDs.
2021-05-25 05:28:02 -07:00
390c21b53e Add image converter to /outfit-urls
Adding the tool that will help people convert one image at a time!
2021-05-25 03:35:32 -07:00
9ea8245208 Serve pet name as id too in GQL 2021-05-12 22:52:58 -07:00