Join me live as I begin rebuilding Jagr.Co with AdonisJS. In this stream, we work on finishing up social authentication using AdonisJS Ally. We'll also add authentication and project/permission settings. Lastly, we work on uploading and deleting images to Digital Ocean Spaces and binding those images to our posts.
Transcript
Looking for something specific? Feel free to search the page for keywords to find a specific portion of the live stream that interests you most.
in the queue um my dog kept bringing her bone to my feet
and chewing on it so then the audio was getting picked up so um yeah so we're going to be continuing
on with the uh jaeger co rebuild um so not much changed since the last stream i
did go through and kind of set up the image uploader um
it's not 100 done yet but so i created an edge component called file manager
that uses inertia to kind of create a javascript state that handles drag events drop events
change events and essentially allows for the upload of multiple images
and then that is just used in the create or edit i don't have it actually
persisting onto the post yet and i don't have since i
don't have it persisting to the post i don't have it actually here it is
setting existing images onto it so it would reset even if they were saving onto the
onto the post so that's really all that i changed um and i did ver verify that
the social authentication um issue that we were having was because
of the i got the um i i got the environment variable wrong for that so i got either
the secret or one of the other ones wrong on it so it actually does work now actually i
think i already have my user created yeah so
you can see here's the user um and the username actually comes through
as my full name instead of an actual like username like this would be so that's what that
sluggification was for so i need to go through add that back in but
let me delete that user and show you that that is actually working real quick so we'll delete
me and come back to and i never did fix the scroll issue my bad i completely forgot about that oh
cool okay my work around's still in place so that works um so yeah go ahead hit the g button
hit my email and there we go so we're logged in now um this is just a placeholder avatar i don't have that
it's not per account it's just a hard-coded string if you're logged in um and so now if i refresh this we should
see there's my user so i need to delete that yet again but you can see that whenever you
authenticate via social auth you don't have a password but you do have access tokens
so that's why the password needed to be nullable um whenever i was making that change in the last stream so let's go
ahead and get the username set up um and then we can go ahead and test the uh
github authentication as well so i actually forgot to do my pre-check
work and find a package to use for this so let's go find a package um
i'm sure javascript would work just fine but we'll go with whatever so this uh the the second result was the
one that i was using before that i was getting import issues with so we'll try this one i looked i don't
believe the adonisjs slugify that we're using for the post
no that's on the model that we're using for the post slug right here uh exports its logification
sluggify method i don't know why i'm calling it solidification so that's why i'm looking for a
different package because this time we're going to be doing it specifically inside of a service before something's saved
and it needs to overwrite any existing value so i could manually bend the
adonisjs slugify package to do that but i think it would just be easier to find a
separate package to do it so this one looks fine let's go ahead
try to find what its package name is probably just yeah sluggify okay
so we'll try this one
okay and we'll go ahead where was that that was in
oh i also cleaned up the services so you know how i created an http context
service so i renamed that to base http service and i moved all of the ones that
are going to be extending that inside which currently is just auth social service inside of an http folder within services
that way it's kind of structured similar to the controllers right so any service that's going to be explicitly for http
requests i'm going to put within services http and it will extend this page base http service that then
provides the http context to that service via this so all these other ones i think i just have
as static functions at this point in time so yeah and then this is the asset service
so now this is something else that's new as well
um i think all this does is apply sharp alterations which i don't have in place
yet so that's something that we might be getting around to either this stream or the next stream um so essentially i have
my images to where i can request them so let's go take a look at that let me show that real quick um so
if i inspect this image you'll see it's doing image and then
it's got my uid and then it's got like the image file name and it's got this uh query premium with a w equals 1100 which
is really i need to change that that's really big for that slot um
but it's also got source images um altering it to web p altering it to
different sizes um actually it's probably using this 550 right here
so essentially i can tack on any other width and
this service will then take that and apply a sharp alteration to it
and then either i don't think it's in the service but it's in the controller so an assets controller then on the get
show here there we go i was previously using node cache but i'm going to switch this to redis
but essentially it will cache it it will it will save whether or not the image is cached
the cache will actually save out onto my spaces i use the digital ocean spaces to save everything
and it will save that altered version so that cache is literally just saying whether or not a
cache version of that image exists on spaces hopefully that makes sense uh we'll get into that probably
i i doubt it'll be at the end of this stream but it'll probably be in the next one um
but yeah as i was saying let's go ahead and finish up the social auth
so social ah service what was i gonna do oh sluggify right so just import
slugify from sluggify that was a default export yeah
yeah okay and then down at the bottom here i had this commented out because i felt like i might need it
so all we need to do is okay cool the file name's the same we'll check whether or not we need to change
any of the arguments to that here in a second and then i also need to import the database module as well
and then we'll just do a like to determine whether or not you will get what the latest
incrementation of the particular username is so like if
i were to sign up with google and it tried to save my username as tom gobbage and then if i were to try to sign up
with github which uses a different email it would then you know save as a different user
but my name is the same so it would try to save tom gorbach as the username again this will check for that
and if tom gorbach already exists as the sluggified username it will tack on the
occurrence length to the end of it similar to what the adonisjs slugify package is doing just this time we're
doing it inside of our own service function so now all we need to do is oh look at
that even had this just commented out so await this dot get user name
and then actually you use this so it should just be a matter of that i
think why is that red username exists oh it's it's voidable
are you nullable oh no it's oh i'm not returning okay well that would help
return actually i can just instead of setting resetting username i'll just return it
okay yeah there we go now it's good so let's go did i delete my user we'll go ahead and test this again i did
yeah okay so head back in here we'll come back to sign up
we'll try google again okay expecting one binding saw two
i'm gonna assume that's something going wrong with that sluggification
expected one binding saw two
pardon my dog um okay
so let's make sure that that's actually just returning back a string
and then we'll see what we get here as well
so we'll go back try this again see what we got the consoles
um yeah so we got the username so it must be on that db query so i think i said this in the live
stream but this was previously specifically for my sql so i don't know
i'm very used to my sql not all that used to postgres which is what i'm using now so
i i feel like it might be something with that query let me try taking let me try altering it
real quick just to see so we'll do const currencies equals
dot and then we'll just do where is there yeah we could do like just a
simple where like to test this out so where
user name is like
and then
let's do well so our alterations only on the end
so we would only want the like uh wildcard to be on the end and then we'll tack username right in
there i think that should do it so we'll see if that works um i have a feeling it's something with that r like
yeah okay there we go and so the slugify did not lowercase it i would
prefer lowercase but that's not the end of the world we'll see if that's an option real quick i guess
oh yeah lower um replacement remove remove characters that match rejects
defaults to undefined trim
okay yeah let's go ahead and set lower to true um
well i don't know that's not the end of the world though yeah and we'll
there yeah we'll keep it for now keep it as this and what the heck i'll go ahead you see
how i'm decisive i am with this sure lower is true um and i'll go ahead
and just keep this for right now it's probably not exactly what i'm looking for
but i will do a search later on to find what the postgres version of that r like is
okay so we have our google version working
let's verify whether or not github is working
cool github's working and you can see the username would have come back the exact same
i actually need to do a unique check to make sure that because i want that to be
unique cased as well so that would come down on the validator
so nope that's post store so the actual validator for this is in line so it would be within the auth controller
and actually it doesn't hit that does it
nevertheless that unique check also needs to be uh
case insensitive so let's see
that would i guess it would be right on my query where i previously had this right
so actually since we're lower casing it to
begin with that would automatically take care of that for me so let's go ahead and just test it let's get rid of the google
and then we'll try to create it again just to make sure that the actual incrementation is working forgot how to delete it's the delete key
tom there you go so we'll sign out we'll sign back up
with google cool all right so you can see now it's tom goodbridge with a one at the end
because now it needs to be unique we already have a time governance in there so it needs to increment itself
so that was that that's working good um and then you can see the avatar url's
coming in with this as well so why don't we go ahead and make use of that and then we can go
ahead and tack a gravatar default on the one that's not um
using social auth excuse me
so see we can normalize this within the model by using a computed property
sorry i need to do a bigger cuff um so let's go down i like to do these
before all of the um relationships and all that do computed
what i think this these are public get if i remember right um and then that was saved in databases
avatar url so i think i'll just name this avatar and then let's also install gravatar
i think that's the package name okay and
i don't remember how exactly you use gravatar i know it's really simple but i think you need to pass the email in a
specific way just spell that right yeah
okay so we'll do if this
avatar url then we just return the avatar url otherwise we need to get
the gravatar um
no i'm not using it is that it i didn't specify node nope that's it
okay so it looks like it's just gravity url
and you pass an email along with any options that you need um
okay simple enough otherwise return gravatar.url
and then we would do this dot email and
pass in i'm gonna assume s is the
size so be the square size that's probably probably probably 40
it'll be okay it's not that big um so we'll give that a save and then we'll head into the
header is it just header i think it's just a header and we need to find it
nope that's not quite it here here's the off check so it would be
this right here so instead of doing that we should just be able to do
off user and then avatar
so give it a save let's see if it works yeah okay cool so there is my gmail
avatar it's really old um and then we'll sign in with and i did
not apply that fix here but let's see if i can get rid of devtools if i can reach it
all right yep there's another really old picture of me and then we will try our gravatar user
and there is our gravatar i'm i'm sure there's like like the specific show
different image than just the gravatar logo but that works for now all right so all of
that seems to be working okay um let's go ahead and
so right now we can be logged out and still head into
the studio posts if i can spell it right we can head into there um
and we'll just get errors because it'll actually try to fulfill the request but there's no authenticated user to query stuff off of so studio in order to reach
it you need to be authenticated so let's go ahead and add that in
i'm gonna minify all this stuff get it out of the way for right now
and see do we already have a middleware i cannot remember whether or not it comes with a off middleware it does
all right so our redirect 2 is not quite sign or log in
but it's sign sign in so we'll change that and
everything else should be okay so we'll head into our routes
if i was smart i put all of this inside of a studio yeah
this will attack a middleware onto this it's going to need more than off so i'll
put it inside of an array for right now [Music] and then since
yeah okay i commented it out here i think just for testing but we'll need it on the assets api as well
so we'll give that a save [Music]
and unable to connect oh yeah okay i gotta add it
thank you error so we'll go into the kernel i'm gonna add it into a named one
what am i using today is off i'm using this off import
app middleware off okay
uh royalman royman sorry i'm horrible with names um
it says can you please tell me how to handle file uploads and requests in adonis js so
that varies based off of what exactly you need to do so like i would have loved to have used drive but for me it
was just easiest to copy over what i had last and then work on upload updating to
using adonisjs drive at a later point in time um i'm handling it 100 manually so i'm
actually reading the streams so you can see i've got request multi-part on file and i'm grabbing the
file via its name of image and then i'm getting the extension
creating a custom file name for it my byte size doesn't work i never
bothered with figuring that out because it's not that big of a deal to me and then i'm uploading it using my
storage service i know that this is probably not the in-depth
level that you're looking for but then i have this storage service
which points to what directory within my storage i want this to be saved to
so if i'm in development locally here i have everything saving to local so that i can just purge it every once in a while and
then anything in production saves to public [Music]
and then i'm using digitalocean spaces which is very similar to just using the aws sdk
so i'm actually using the aws sdk to upload so everything here is essentially the
same as you would use to use the aws sdk but if you're not
looking for that level of control
there is a library adonis has it's a first party library called drive
i would heavily recommend going through this documentation page um
it makes files very easy to handle um
and i mean really all that you need to do is set your configuration you don't have to read the file as a stream like
i'm doing um so i would definitely recommend this approach especially if you're just getting into file uploading
and it it supports s3 like i said i i'd love to get my code over to using this
in the future because it just makes things every it makes things entirely more readable than what i have so
yeah i definitely recommend going through this documentation page for that um and i'll have a separate lesson on that later on in the let's learn series
um did i ever fix this yes okay
so now if we try to go to studio
and posts we should be redirected right back to the sign in which we are
and then if we actually sign in and now we go to studio posts
we're allowed so that works a-okay and that should be for the entirety of
studio which at this point i think we just have the posts right
yeah okay yeah these aren't even go on actual pages or just go on the hashtags so
um all right so that was easy what else do we have uh let's see let's take a look
at my list so social authentication not completely done we still need to add
in the profiles i meant to before this stream go through and
finish up all of the models and migrations i was i didn't want to
bore you guys with porting all that over um but we'll see
and then add auth and real check to studio yeah so actually roll so
the way i have it right now is if you're an administrator you see the dashboard you see posts series topics and comments
and then if you are not an administrator you see settings and maybe something else maybe you do
get dashboard but dashboard was never completed so i always i always save dashboards for
last um it's just a habit of mine i guess because well you
need stuff to actually put in it before you can actually do it but then i just never get around to it so
um yeah so let's go ahead and do that so if
you're an admin you should be able to see post series topics
you know maybe comments was public too because then if somebody replied to your comment it was visible there
so we'll just do post series and topics if you're an admin for right now so
let's do a new middleware
and we'll call this and we'll do it unspecific to a roll
we'll specify the role whenever we define the middleware so i don't want to just name it admin um
studio access no no no no it needs to be a rule
sure it works for now it's better than ever thinking uh so
let's see let's go ahead this is going to be a named one so we'll go into our kernel
roll import app
middleware and roll and then [Music]
um let's see did the arguments come in directly in here i don't 100 remember i
think they do no that's next that wouldn't be it
hmm i'm gonna look don't mind me i'm gonna look
see if it's in the documentation real quick middleware middleware middleware
okay okay that makes sense it was the next one so right
guards passing name one time yeah that's it cards
which is a string array yes okay
we're gonna make that required um
so okay
what we need to do is i have my rolls as an enum
and what we need is the authenticated user so we'll do off
so we'll do const auth role id equals off user
role id if
yeah well then we need to get the guard role id um
let's see rolls
yeah so we'll do
object.keys match it to the key
is it roll just roll that
did not seem to import from anywhere let me go and just import it directly so import
name it rolling on from app enums
roles rolling on dots yeah no just rolling
them so there's the those will be the keys
so that will work um and then we will map it
to lower case okay and then we will do
well no that's not the guard roll keys that's the that's just the roll keys and then we'll
do const guard keys equals
guards dot map
and then if
i have a tendency to overthink things whenever i'm trying to also talk through it
objects possibly undefined it's fine
so really what we want is not just the roll keys that match it against
the guard keys we want the roll key that the authenticated user has
so const off roll key equals roll
keys what [Laughter]
okay i'm overthinking this the const just a simple search time
cost off roll key equals object dot
keys no rolling on roll
a num dot find
key.2 lowercase equals but no you need the id
so we need values of that which then this is a number
so we'll do id id equals auth roll id
in which case we will return back yeah that just worked that that'll work fine um
authoral id oh no that is duplicate so instead of doing this we'll do off user
rule id we'll get rid of that okay
is that even pointful why am i doing that i'm sorry
const
all right you know how to do this i don't know why you're having such a hard time here so you want to get the id that
the authenticated user has and you want to get the key for that
well why not just turn these into the ids that would be easier tom come on all right so you have you
have the guards so then all you need to do
is return back a map of roll enum
and then that is that yeah k.2 uppercase
that works for the roles that i have so that's not the guard keys but the guard ids
guard roll ids and then if off user
roll id i need to do includes so if
guard rule ids includes
oh my gosh finally got it sorry that took me so long mental lapse
uh and then we need to do the negative so if not then we need to throw
an exception my goodness so uh
yeah well yeah we'll make an exception for that so what that'd be like not authorized um
not allowed what's that i don't quite remember the verbiage for the http code
uh four oh [Music] unauthorized yeah okay 401
so
there we go i'm not delete not authorized i was close
oh no exceptions all right and
goodbye okay and then
i don't quite remember what i usually do for these off the top of my head let me go look
so i know i already have one i think i just pass it to the super
oh so here i called it not allowed yeah okay so i just pass it into the super
key easy enough oh that was for a 403 though so we'll do okay and then e
unauthorized there might already be an adonis
and then all that we need to do is whenever we want to throw it just pass it in a message so
here throw new
unauthorized exception
yeah i'm gonna just put a default message in place so that we can just provide it like that because uh for
the most case that's gonna just need to be the same thing you're not
authorized to perform this action okay
there we go all right took me forever but i finally figured out what i needed to do for that
and then let's go ahead and down in did i already add it yeah okay so roll is what i called it so
now we just need to apply it to just the
specific studio here studio so posts so we'll just tack it on to
each group as it's applicable so that would be post a series and topics so do middleware
um roll and then what is it is it colon or is it
just comma gold on commas all right so then this
would just be admin so we'll test test it out see if it works i don't think i changed any of my current users to
admins so we'll also need to do that
where's the role id oh here it is are they all admins what's one
one's most likely basic just a normal old user where's my enums
yes one is user two is admin so we'll go ahead and make google the admin
since that'll actually be the account i'll end up using so there we go
and so i'm logged in currently as
i don't remember so we'll just sign back in uh we'll try test user one first
good old test user to test things out so we'll also need to hide these since they're no longer applicable but
whenever we go to posts there we go there's our e unauthorized you're not authorized to perform this action
so so far so good let's go back let's go ahead and test google
okay now we should be able to see posts oh no
i did update it right i did push the update i might not have let's try that again
give it a refresh just to make sure everything's good okay yeah okay there we go i guess i didn't actually update it so now we're good
there um and then for sanity's sake we'll test github as well
and there we go okay cool so let's go back to google and we'll verify that we can still
no i don't want to sign up i'm going to sign in that we can still create posts and all
that stuff so we could see the create post form um if we publish we should get back an
error which we do if we create one with the verbiage of testing
all right cool so everything seems to be working okay delete did not delete so i guess that's the
next thing to look at i don't know if i broke that whenever i implemented the table i'm pretty sure that we had it working
so i must have that or it could be something with the admin check but i doubt it
um that wouldn't just break that so let's go take a look and see why that delete's not working
so that would be what views studio
index all right so we have a four method of posts
and that's providing the actual method as delete posting to
studio post destroy with an id of scope row id
well off the top of my head that looks right so
let's i suppose see if
so let's head into that delete call and let's see whether or not we're actually even just getting
there if we're not getting there and then we'll try to remove the middleware if we are getting there then
we need to check and see what heck the param is so we'll do here and let's go ahead and just pass in the
params as well we'll just go ahead and try to knock two birds with one stone
uh so then we'll hit delete let's check and see whether or not we got here we did not so it does look to be getting
blocked by some form of a middle oh you know what did i ever put the csrf token in that form
that would be a good reason why it would stop working randomly
i did not so there you go csrf field
all right and so i don't have any toast system to
notif send notifications quite yet so i don't really have any way to tell myself exactly what the error is there
but all right let me go back and back
okay there we go all right so yeah now it seems to be something actually with the post so it
might have been something that i broke whenever i um
probably something with a route so let me take a look at the underlying
route that that delete call has and see what the final one actually generates out is
well yeah that would be an issue wouldn't it it's just a form
it's got no submit so either i put something in here wrong or
i'm having a oh wait a minute what's that extra form block doing there
that could cause some issues all right let's give this another go
there we go now it's gone okay so if i didn't already let's go ahead and get rid of that console log that i put in that post controller yeah
okay
cool so that is a happy edit it was just a stupid issue that i had with that um
let's see what next so social auth is working
uh we added an off and roll check we need to actually finish that roll check so
um because if i sign out sign in and
use a non-admin account if i go i can still see the the pages that i'm not actually act that i don't have
access to here currently post is the only one that actually links anywhere but
ideally that shouldn't be showing and then if i actually go into well none of these other pages actually
but if so let's go ahead and just add studio or
settings let's stub the settings page real quick because we'll need that eventually but like if i were to actually go into settings then within
that little studio header i also need to hide it there so we'll add settings in just so i can actually
see that with an authenticated user unauthorized user i mean
so nope outside of posts so we'll do route group
prefix um settings as
settings uh we already have the off middleware because it's in the outer group
so then we'll just do a route.get for the index page for right now and do i have a controller for this
no so we'll need to create a settings controller as well
and we'll just do that okay let's go and create that controller really quick
okay oh i meant oh well we'll just drag and drop it
forgot to prefix it with studio um and then oh and i forgot to mark it as resourceful
that's all right it's not all that resourceful public async
index
okay and we need view
and we'll put this at studio
settings index yeah that'll work for now you can always change it so
that should all be good now we just need to stub that page i'm just going to copy the post index
and then we'll just delete the table so studio
settings index edge
oh i copied the file not the contents of the file okay well that doesn't help any
all right and then we need to get rid of that table
so really that's just all of the content minus the negative margin uh which is going to get rid of that as
well and text settings on there and that button needs to go away
i don't know whether or not that will have any button and then the heading will change from posts
to settings okay so now with our non-admin user
we should be able to go nope i didn't actually rig up this okay well let's go ahead and just do that real quick
header did i put these up to the um yeah okay
nope that's comments settings so route studio
settings index save missing token
okay so now we should be able to go down to settings and yeah so ideally
since this is not admin user we want to hide post series and topics and just have it be dashboard comments and
settings so
we also need to do that for the drop down as well so the easiest way to do that for the drop down is to tack on whether or not
the user is a admin admin however we can do that at a more global
place so that we can we don't have to actually perform that check in each place that we need to do it
um i don't remember what i usually do for that so let me go take a look what i usually do for that and then we'll see whether or not we want to continue with
that approach because i know i didn't have the best approach for it with the old project
but it doesn't hurt to actually look and see what i was doing actually i think i just placed a config
did i did i did i nope oh wait settings
nope not not quite okay
well i don't really remember what the heck i was doing with him i could always just do something in the
provider for it
do something like user settings why don't we try that approach
there's always that adonis package i've yet to use it but it does
it does permission checking i don't quite remember the name of it off the top of my head but it's in the documentation um
but i'm gonna try something i'm gonna try putting like a settings within
my actual so we'll do we'll do this within a service and then we'll just instantiate the service as a container or something like that
and then also pass it as a global to the view [Music]
so services this will very much so be an http service and
we'll call this um settings service sounds sounds
appropriate for right now so export default
setting service extends base http service
i mean you cannot find name setting surface oh i always do that forget the call to class
okay and then um
so we'll do i guess just getters off of this would make sense so we'll do get
permissions um
and then we will just return back a list of permissions so
there well
yeah let's change this from yeah
so then we'll do access
um let's see what's the best nesting for
well we can we let's just roll with something and we can change it as we go if we feel that this is too verbose or
something so studio yeah that's definitely too robust because you would be doing settings
permissions access studio can
all right so let's just kick it back one let's get rid of access so studio
can posts
and then we'll have um public get
is admin right and this will just return back a boolean whether or not the current user
is an admin which we can take out of
the role oh no no we can't actually um i forgot that we didn't just do it
admin middleware um
so then we will just do return this
ctx off well no i think i just put the user directly off of that right
i didn't are you sure i thought i did
yeah get user right there
oh no that would just be this.user not this ctx yeah there we go okay
this user dot role id equals
import really numb from app hems
roles really numb dot admin
all right and then um
yeah i'll go ahead and i don't want to explicitly make that so
well yeah we can make that yeah let that make it safe
so then really all that we need to do is anytime that we want something to be bound specifically to the admin we can just do
this that is admin and if we add any other roles we can you know
simplify the checks by adding a additional is statement up here so can access posts can
access series
and then we can also do can manage posts
can manage series
can access topics
can manage topics
all right i'm gonna put a break in between each set there we go um
and then we will do another one actually i'll put this back off of
here get
is authenticated return this user
wow butchered that return there we go return this user and
okay there is a specific i think is authenticated check um on the off
but that'll work for right now um can
access dashboard there's really nothing to manage on the dashboard whenever we actually get around to actually building
it so we won't have a manage permission
so you can access that if you're just authenticated
and then what's next comments
and this one you will be able to manage comments because they'll be bound specifically to you
and then the page itself will just determine whether or not you see all of them uh whether or not you're an ad so
and then last is settings so well really these are pointless right
they would just be true but because uh we have the off middleware on it but we'll
check that again here just i i don't know for safety's sake i guess can access settings
can yeah yeah sure
okay so then really all that we need to do is utilize these and then once we get into the front end stuff we can add
non-studio stuff in here as well and then we'll have other middleware
checking whether or not you're an actual owner or admin of a particular post series
or whatever um for what since i'm the only one that posts to jaeger right now
that is not very necessary because i'll be the only one that ever has access to the
post series and topics but um back whenever i had the multi tenancy
and other people could post and all that stuff um that was very much a useful thing
so i'll put it in there just in case i ever change my mind in the future it'll be there and be ready to go
but we'll worry about that later on in the development so now we just need to utilize these
and actually create the provider for it so within our
provider here we can either well it wouldn't make sense to register it as a binding we can put it on our http
context if we want i don't know if that fully makes sense
for right now we'll just leave it as a service but it would be good for the view to have access to it
yeah we'll go [Music] yeah we'll go ahead and put it on the http context what the heck
um let's see so that uses
yeah okay so we will do
construct settings service
equals import
amp services
http settings service
and then we will do
i'm blanking
see this is a real reason why i make these lessons so that i can refer back to no i'm kidding um
let's see so this is a group
did i title this one wrong
i want to add a global property to your http oh no here it is just got to scroll down further down
so
ah gotcha
okay i gotta do it in a middleware
good old brain farts all right so node ace make
middleware um oh what would be a good name for this
there we go just call this app state
and then within our
kernel will make this a global
middleware
actually i think i think i have this on my previous
project as well middleware app state so
here we're going to want uh the request no the route
route um well we're going to want to just change this to ctx because we're going to want
to place something on it so ctx okay
if
ctx dot route dot
no request dot
method equals and then
i'm trying to think yeah let's just put this one on all of it so
import setting service oh thank you
and then we will do const settings service equals new
setting service no that does not need that um
and actually we'll just do ctx dot settings
equals new setting service and then we need to create a new contract actually extend that and then we'll have other things in
here as well something specific to get requests um
so it will be more than just setting the setting service here which is why it's named app's date so instead of just uh
setting service middleware um so contracts
i do not already have http context in here so we'll create that
no not s
there we go declare module
http context contract and then
settings how do i set
this to an instance of a service
no http settings service okay
i doubt that'll work but yeah
because i don't think it's just like i don't think it's that it would need to be as specifically an instance of because i
think if i set it like this then it would be looking for static properties on this right
i don't know we'll see if that works i don't think it will but we'll see if that works i think i need to explicitly tell that's
this instance of it um i don't quite know how to do that with typescript
so we'll try this we'll see if this works so all right so if i do that it seems happy
with it it seems not to be complaining but
yeah there's permissions there's okay yeah okay
yeah so that seems to be working um
cool so then all we need to do is
it into
okay i'm not 100 percent enough that'll work normally anytime that i end up view
global i'd do it within a provider but
i would think that that should be okay because that definitely gets reached before the view renders but we'll see we'll see if
that works so um within our header then we'll go ahead and do that drop down
first so we'll do
um
show i guess
yeah that'll work and here then we would want to do settings
studio can
access dashboard start with the dashboard we'll see whether or not that works or throws in
there and then we'll just tack on a show true for the rest of these real quick
okay and then we will apply this
if we can find it right here okay so
really just wrap this up in an if so if route dot show
okay probably even better just to filter the studio routes
when i think about it rather than actually tagging an if on it let's see if
dot filter um r dot show so we'll see if that works
cannot read properties of undefined reading can't access dashboards so studio is undefined there um
i had a feeling that might have been the case so let's see what we got going on here let's see whether or not settings
is set at all or whether that's something else entirely uh so i will
replace cut this out replace it with just true for right now
and then drop a drop and inspect here
settings and we'll see what we get
oh no well i was not expecting the whole http
context
why would that be happening
crew
look and make sure i'm actually setting that right so view views and templates
that does allow an airplane sorry about that
actually it's probably in the reference no no no it wouldn't be that's that's
for that's for um actual usage this would this would be more like the
view module um well tell you what actually let me go
i know i have a ton of them on this project so i'll just poke in here real quick
and all of these are set on the provider yeah yeah so i should be able to just
set it directly the way that i am so it might be the fact that i'm doing it within a middleware but i don't exactly
know why that would set like why that would come through as the whole http context as you can see here you got ctx
let me try const
settings equals new settings service
ctx settings equals settings and then instead of setting it off of the ctx let's just do
settings no it's no different
[Music]
all right let's see whether or not we can actually find settings on here then uh it's circular ah
oh um yes that uh that's a very good point
it is extending the base http service which
provides this ctx so that is very much so circular so we don't want to do that
at all
so instead this would probably be better as a builder or something of the sort that doesn't return back necessarily
itself but something scoped within itself um
so the public want damn yeah no no don't do public um async
no it wouldn't need to be async so do public provide a build method on here
and what this will do is it will just return a cherry picked portion
of its settings um that way that http context doesn't get carried through on and it's not
circular so that this will work for right now this is a good workaround for right now um so we'll do
just cherry pick some of the things off of it
all right not exactly a builder but
it'll differ so app state then then instead of doing this we would want to
instantiate it and then call build yep in which case that's going to
complain because the type no longer matches so raises a whole other question and
probably just define a separate interface for it right
yeah i would i would think so
because i don't think that you can do can you
i'm spitballing here i don't know if that's valid doesn't look like it a const initializer
is an ambient context
okay yeah
so i don't think that is valid so we will do another i can either add it in as a contract
where i can put it directly in the service probably just do it as a contract i guess
let's see so um settings.yes
and then what um oh nope not not you
you no not you where is it
so sitting service there you are all right um
so we'll probably need to define a type for permissions so that we
can specify all of these booleans uh or we could grab that type right off of
am not the strongest with typescript i have not spent a whole lot of time devoted into learning it
um so if i'm doing something that is
absolutely way too over the top just to get something done here let me know in the comments i'd love to
know so let's see so settings would be then is
admin which is a boolean is authenticated which is also a boolean
and then permissions
which again i don't know how to get that instance of
settings maybe i can do um
yeah no okay where's the shot
can i copy and paste that tooltip that showed up before that has all that defined for me already
copy it and pick yeah all right cool let's save some time
don't like that that means that that's a manual list but i don't know how to go about doing that automatically
at least not off top of my head without doing some digging so then this would just be
permissions and then on our http context instead of doing that we would want to i guess
do i need to import that contract import
see but neither of those were default but we would just need um
settings from
contracts settings is that that good yeah and then that becomes type of settings
yeah okay and then we need to check our app state and it's still red
types of property permissions are incompatible
all right it's inside of the studio that's so okay it's back down to
settings so
this would really be yeah i don't know can i do an object with
a probably but it would probably be easier to just do
that
okay are we are we clear now good
all right so that was a big
loop all right so um back into what is it our
header yeah let's verify that now our settings are our settings and not the http context
so i'll probably give it a read yeah all right cool there it is
and so actually we can see i should be able to access well no all of this is just defaulted oh no these
are the actual settings not what i have defaulted so we should be able to access the dashboard we should not be able to access posts series topics or manage any
of them then we can access and manage comments and settings so there we go
i'll leave that inspect there for a second and we'll just go ahead and apply these so that would be
settings studio can access
dashboard then this one will be settings studio
can't access posts
can't access series
can't access topics
you can access comments and finally
can access settings so we'll give that a save
oh man i was so confident in it
okay let's undo back to all trues verify for a second
here permissions permission studio okay
so it should be settings permissions so we'll do uh find and replace for settings dot
one two three four five six seven yeah okay
and then just remind me to fix that route because that's going to get matched too
so replace that with settings dot permissions dot
not as short as i would love it to be
but it's okay for right now we could also add in a getter as well so
we could do like settings dot get can access settings
um or settings that studio get can access settings or something like that but
this will work for right now can access comments i must have named that something different
oh no i didn't spell studio right there we go
okay so now if we do the drop down you can see now we just see dashboard comments and settings so now we just need to do
the same thing here as well so i'm going to go ahead and get rid of
that inspect now it served its purpose and i'll probably go through
some point later in a week and clean up the long key that we need here just to check out
permission make that a little bit shorter so i think that's in the settings layout
or studio layout oh no header studio okay
okay and
good enough so now i just need to replace this true with that setting strip
okay
appearance is turned off off so we'll skip that
that's not even in the current code base that was in the code base before it but carrying it through
so back when people other people were allowed to post there was it never quite made it out to production
but there was a full set to where you could select your font choice and everything like that so select your main and secondary colors
your grays and all that fun stuff so um that's what the setting the appearance page was for
um okay so that should now be good at well no well eh
so all we need to do is tack on that filter to the end of this as well and then it'll be good so filter
rr.show active no show yeah
okay so there we go so it's kind of minified but well not minified but you
know smaller dashboard comments and settings is all i can see so now if i log out
and sign in with google we should now see all of them so there's yep dashboard posts if we go to settings
we can still see all of them and we still access posts as well
okay so what next should we tackle
we got often roll checking done for studio
um well why don't we
start with binding the image
to the post and then we'll start to if we can get that working in a timely manner we'll then take a look at
pre-populating this image selector with any um already
images already attached onto the post so we'll try to take a guess at it first
before actually testing it so let's see here
let's take a look at first what i have the file manager actually attaching the
image as like the name
asset ids okay and it's an array of them so
on our post validator we need to ensure that we have asset ids
somewhere in here i guess also i need to verify that
that's one of the tables that i ported over yeah asset posts and assets are right
there okay so
schema.array i guess that would be optional because if you don't provide anything it should
be all right do i have this double defined why are you giving me a red squiggly here
remember so yeah okay
are you taking away where is that go look real quick i don't fully
remember what the array validator type takes validator array
yeah i didn't think it would need a whole object okay
dot number number number and then um
we would also want to do a rule check to make sure that the actual id exists that we're passing in for each
one of these so we'll do rules dot exists
tables assets and column is id
okay so now it should be coming through on our validator
then on our posts controller
see we'll want to do store first i suppose
um instead of tagging it in with data and the like we would want to do asset
ids extract that out of the data and then
where we're also attaching in our posts we'd want to essentially do the same thing with the ids so we'll await
auth user related assets oh did i not
define the relationship here oh no this is from the user sorry
await post dot related assets there we go
dot attach and then this should already be an array of our
ids um
but we have intermediary data i don't remember if
it's yes i do need to buy a license for you um
sort order i don't remember whether or not that has a default value actually i can check real quick structure
if it no it's not nullable and it does not have a default so we will run into issues with that so we do need to provide
that um so
we need to create that structure and once we create it then we'll extract it out into a service because it's not
going to be the prettiest looking thing so um
do const assets sure
equals asset ids dot
and then we'll want to reduce it um
all right something in the middle and we'll start you off as an array
so and then we also need the index that's the third and then reduce right yeah
so uh essentially what we want to do here is on
we want to return
and then we want to tack on the current as
trying to decide how much of this uh see so we want to do we're going to be doing it via the post
so we don't need to provide the post id but we do need to provide the asset id and i believe that needs to
be as it's defined in the database yeah it doesn't hurt to define it that way not that much
so we'll do that and that will just be directly the current so this would be better
labeled as current id so we'll do current
id and then the sort order is what i call it right
uh yes sort order as just
i so it'll start at zero and then as it loops through it it will increment it as
needed um yeah so that should be
all we need for the assets and then we will do a
post related assets
not sync for saving so detach would be pointless but we will attach
and then it's already an array so we should be able to just provide in assets and it's not happy so what aren't you
happy about here um asset id sort order
array of undefined is not assignable to parameter of type string or number array
let me go take a look at what i um
i thought it was just um no it is an object isn't it it's the
object the id is the key uh yeah so
i need to go take a look at that remind myself how that works so database down to
many to many should be something like save pivot
attach maybe maybe it's an attach yeah okay
yeah so not quite an array but rather an object where the key is the um okay so
let me get rid of that const asset
data equals um asset ids dot reduce
previous current id and index same as before
and then instead of starting with an array we'll start with an object and then we will return
a spread of previous and we will add on the current id
and that will have does that need to be
well i'll define it as is defined in the database sort order of our i
think that should be it um we can actually clean that up a little
bit by getting rid of the i don't know whether or not that would
actually be cleaning it up just making it even messier but uh we'll try
that let's see yeah it's not horrible
okay and then instead of assets here it would be
asset data and it's still red what am i getting wrong now
argument of type object or undefined is not assignable to a parameter of type
string number
should be an object though no it is an array isn't it no no no it should be an object to get
an object right right
we're going to try this out okay so let me copy this
i save that as asset ids right yeah oh man i fat fingered it
varian says uh i've been using this framework for about five months in development scheduled to go to
production in january sorry for my english is good um that's awesome
hopefully you have a smooth transition transition to production whenever you do launch
um i guess i need to type this all back out
let me go ahead and just exit this so that all that gets cleaned up then i'll go back into and try that once more
okay const asset ids equals array of one two and three
okay const asset data equals asset
ids dot reduce previous
current id and i and then we have returning an object
if i hit enter is this going to damn yeah
all right let's try that again nope shift enter doesn't do it okay well let's just keep typing them um
prev otherwise current id
as an object with sort order
of i and then
hopefully i got that right
did uh did it work i don't think it did otherwise we would
have gotten undefined back wouldn't wave
let's see if it saved asset data is an empty object
okay
all right well
maybe i am misremembering how to use reduce
where's my post service there it is so in here i think i have a function called sync circle sync posts and sync
series and all that stuff so i'm just gonna cheat and
sync assets ids reduce
yeah that's fairly similar to what i'm doing already
maybe i just need to call sync i don't know [Music]
i am doing some checking to make sure that the uh array is an actual array
i'm going to copy this and just compare it and see how different it
is so reduce previous
term pre yeah that's that's the same
so it could just be visual studio code not quite cooperating with me it could be that
this needs to be sync
let's try sync
i'm going to try manually defining something in there and seeing whether or not i get the red squiggly there too
so let's change that back to attach so i've got a feeling it's just visual
studio code maybe let's do one with a
nope nope that's very okay it must be something i i don't know
that's strange
well i mean that's trying to return back an empty object or undefined
i'm gonna cheat see here so post service
oh passed it sync uh nope did not quite pass it sync assets right here
and okay yeah so we need to get rid of that undefined is what it looks like so
it's visual studio code tagging oop that's the wrong app attacking on that x or question mark i
think causing the issue so if we change that to an exclamation yep okay that is indeed
surprised my head didn't go there
so then we would want that to be within our validator to either be required
or we could default it to an array if it's not provided um something like that
i think we'll go with that approach because the assets aren't required so we'll go
with that approach okay so that should work for the store then we just need to do the same thing for the update and
instead of copying and pasting that directly um again we will move that out into a
service which i don't think i have a post service
quite yet so we'll go ahead and create one of those
okay
and just so that it can be used for both the um
store and the update i'm going to instead of calling attach for storing
and sync for update i'm just going to call sync for both it won't hurt anything um so actually i'm at the caveat so
we want both of these so we'll want to cut that out completely
and sync assets we'll define what it takes here in a second
so first and foremost it's going to need the post and it's going to need the asset ids um
so i would say take the post first and then take the asset ids
which is where we can now define this as an array of numbers or
default it to an empty array so we'll do number array equals empty array in case
it's not provided which allows us to clean that up to just back to asset ids and then this also needs to be async
and then that should be it i think right yes
okay then we just need to call it so a couple of ways that we can get this
into our post controller we can either manually define it on the constructor
or we can use the ioc container to inject it so do object there and then we will stub our
constructor public posts
post service type post service and there we go so now our post
service is available via this so we do await this dot post service dot
sync assets post and asset ids
this could also very well have been a
static like nothing in here requires http context so
on second thought
let's get rid of that let's make you static
i i can't immediately think of anything that we're going to need a post service for
that cannot be static or that wouldn't be overly drawn out being static
so to keep things clean let's actually just go ahead and make this static and then since we are no longer using
http for this let's move it out
okay and then we no longer need the bind for this and it'll no longer be on this it'll just be accessible
so we no longer need the constructor so pardon me for that little circle there um so now instead of this post service
it's just a wait post service i don't want to make things more difficult than it need to be
so let's go with this approach and then down here on the updates after
we save let's go ahead and do the same thing so await
post service dot sync assets and we actually need to extract this out of the validation data
as well so asset ids oop there we go
this takes the post and the asset ids so
and then lastly whenever we delete a post we also want to delete any of its
assets so
let's see here i can't think of an instance
where we would because images are going to be tightly woven to the post like
i don't have an image library where the post will be able to share assets so let's just completely delete
the image whenever you delete the post too so that comes into the storage service i
think i have a delete or destroy function on this that actually destroys the
actual thing so we'll want to call that so we'll want to import that as well
import storage so uh does that take the file name
yeah okay so we'll need to query
so why don't we put that in service as well well we'll write it out first then we'll move it into the service so
const assets equals post.related
assets dot
query dot
nah well yeah yeah yeah that'll be all right
um we could have just loaded it but that that that way the assets are directly in here and then
select trying to think if i can both select and delete but i think that would be
counter-intuitive so we'll select just the
file name i think i have that all under case lowercase
yeah um so file name is all that we want
and then asset
file names and then we'll also do await post
related assets query dot
detach and then delete well all right let's go ahead and select
the ids then too let's see
so i don't use cascades so if i just delete the assets it won't detach them um so i need to manually detach and then
delete if you're using cascades then that's really not that's a one step solution um
so then we'll take those ids so we'll just change this back to assets
and we'll do let's see
ids equals assets dot map oh whoops i forgot to put the weight on
the front of them
okay so now we have an array of just the ids so we're going to await because we've detached before we deleted
so now we can't do related to get the related posts that we need to delete so we'll do awaits um
asset query wherein
asset ids delete so that oh no what do i got going on here
oh yeah no i need to specify that those are for the id we could do file names as well but
just in case we'll do it specifically on the ids um
and then we also want to delete the actual asset file so we have the ids here
let's also do const so file names
okay and then at least i don't think i have a storage service solution that takes
multiple it's just one at a time and i don't know whether or not
the s3 sdk has like delete objects where it takes an array
sure you got buckets but i don't know oh yeah there it is delete so let's try to write that up real quick
so i don't have to loop over and call this destroy
i don't know why i have this difference of the file name where the actual is stored as all lowercase but i have this
file capital n names um so it'll be a string array
taking the same arguments from thereafter directory
return back a promise of a boolean just stating whether or not they were successfully deleted
const params it's not the end of the world if they don't successfully delete um i can set up a cron job to check for
any unused assets and purge them so that's it's no biggie if there is an issue here but
ideally it should work okay and then
directory i don't know why i'm not just copying and pasting this from right above but
whatever actually that's gonna need to yeah
let me take a look and see so destroy objects
what do you take so this disc which is the s3
sdk instance um delete
objects all right let's take a look at the other
usage delete objects request
so i need to figure out what all that takes see if i can inspect this
peak
um not gonna tell me let's try clicking into it
oh no that's the output no i don't want the output shoot
peak definition
so we want the other usage where's the other usage right above it yeah there you go delete
object request which takes the bucket which i believe we have
um
bucket name delete all right so delete's got to be an object containing like the file names and all
that right i'll just open it be right next to it all right let's go into delete so i
could have just so objects i'm not gonna remember this we gotta
all right so it takes what bucket first right
delete what was that something delete request to delete object request
objects request yeah bucket and then delete so bucket
delete um
and then objects um i don't know why i would need to do that
quietly so we'll do object
i keep sorry i keep wanting to switch app objects
which is what another object object identifier list which then should
be just the file names right
object identifier key version id so yeah that's where i
had would have then just the keys so this
see if we can would be
no we won't need index
prev and then
no this is an array right objects is an array it would have to be
all right let's start back up at the top delete object request
come on click click it there you go delete objects
which is a list yeah so it's an array okay that definitely makes because i was going to say that would have to be a
keyword for for each of these that doesn't make any sense so instead of reducing here
this would be an array so filenames.map file name if i can spell it
and then an object with capital k key as
file name then i think that should be it then we
should be able to use it the same as just delete object up above up above
so it would be params dots promise to turn it into a promise
then and then i was giving sublime text a test run
which is why i didn't know that that red squiggly was there
but oh well i know that that works so it's no biggie
i'm just gonna ignore it for right now okay
directory oh oh yeah okay so we need to do this for the key not just file name
all right cool so that should be okay so now we should be able to call destroy all
and pass it in an array of the file names so
await don't really need to await this there's no reason to it's not something that's
necessarily required so we'll just do this synchronous or not
synchronously but we won't make the request wait for it so we'll do storage
service dot destroy all
no reason to make the request wait for it here so we'll just pass in our asset file names and that should be it
so that should work um okay
so now whenever this is all for destroying a post remember so
now let's go ahead and well that's not all that bad but to give this section of code a
particular name let's move this out into the
service so looks like all we would need is the post
so post service public static async
well it's doing more than detaching
destroy assets that yep take the post of type post and i think that would be
it because then we're finding the asset off of the post need to import asset itself
and we need to import stored service and that should be it
and then on the flip side we will do await
post service destroy assets and we need to await this
here because we cannot delete the post up until the actual assets are deleted and
detached from the post itself otherwise we'll get a foreign key issue um
and then we need to pass out the post okay
looks good let me get a drink
okay shall we give it a test run so i'm gonna
go ahead
if i can spell try to get in the digital ocean here if it doesn't make me open up my email
cool let's go into my space not the site but my yaeger space
so as i was mentioning like with the sharp alterations so whenever
an alteration is done um that alteration version is stored in this dot cache file
so all of these are alterate altered images
oh other folders so the image itself so that's the image file name
and then inside of it are all of the specific alterations
so that's how that's being done so these are all so like all the alterations for this
image are within this folder and you can see i've this is one of the earlier lessons so
it's gone through a couple of iterations of the site so
and then it would absolutely not hurt anything to just delete this cache folder should anything ever get too big
and then any images still in use next time that they're requested the new cache version
would just be stored in here and this folder would be created so that's what's happening there and the same thing i think is happening
inside of local and then public i think uses that one no no it looks like it has
its own so i've changed a couple of things over
the course of time and i haven't gone through and purged this because i still have plenty of space left in this thing
so um but yeah so let's go into local
because that's where everything that we're working with locally is going to save and then no i'm not signed in with user id one
actually so let's go back out to here because uh
i need to be an admin now to save stuff which is my gmail so that user does not have an id of one
but rather uh two or three so let's go ahead refresh make sure that
we're still logged in and everything looks like the avatar broke
uh that's funky okay i wonder if we're getting a coors issue or something like that all right that's no big deal at least
not right now anyway um so let's save a post so we'll call this
just testing image store
we'll give it an image uh select one of my other old thumbnails
okay we'll do we'll do this one all right so we'll select that so now
that loading indicator was just saying that it's still uploading but now that that's gone it's done uploading so we
now have an id populated within our um
image ids so if i were to do
document forms i think i called this post form yeah
and we do assets actually i don't know how that handles array
fields with this
that would be i'm not seeing it
let's see if title's in there yeah titles in there
so maybe i did not give that a name let's see
here's the input name
is yeah no it's empty so that's well no that's the that's the uploader
nothing's actually stored on that once the image uploads it's then moved
into this loop which would then have
yeah a name of asset ids and the value of 5 right there so
all right let's try just a straight up query selector so document.query selector
input name equals
it's been a minute since i've worked with array-based form fields
maybe i'll just trust it and see let's add a console log in there let's see what we get back oh yeah we can get rid of these now too
um so on our store
let's just do a quick console.log of the asset id see whether or not we're actually getting it back
um fingers crossed that we don't run into an issue with the validator actually
just to make sure i'm going to provide future dated publish ad because i think
i was dumb and i made this required so but just in case we'll provide it
so let's hit it and okay created
did we get console logged out of our asset ids we did awesome did it store
onto so we should have an asset with an id of five we do my user id is seven oh that's right
i've been deleting users so that makes sense so on our space we should now have a folder called seven
with a file name of uh 2021 lb jager011 and then a timestamp
on our asset posts there it is asset with ninety of five
post with ninety of three sort order of zero so if we come back into digitalocean
refresh there appears to be no refresh button
so i'll refresh the page there is our seven folder there is
our file so ql
um now the big thing to do is to alter it to where whenever we go back to actually
edit this we need to pre-populate that so
first things first we need to query it on the edit page show so we can get rid
of that we know that that's working we know everything with store seems to be working so far at least with one image um
as for the where's the edit here it is so now instead of just doing a finder
fail it would be beneficial to
preload some data onto it now we can either preload it or we can load it in separate um
not doesn't really matter it just changes how you reference things on the front
end so i'm going to go ahead and change this from a finder fail to a query
so we'll do const post equals 08 post.query
where id is params id
preload i'm going to break this down
assets and then at some point in time i will be
putting sortable on this so the order does matter so i want to do query
query dot order by da
sort order and we want that to be ascending actually
stick with that and then anything else i don't think so
so on the relationship i should already have that sort order but we wouldn't need to display that in any way on the front end we could just use index no
problem with that actually using an index would be beneficial because then as we update the sort that would
update as well so um okay so now we should have the image
passed through within the actual form so if we oh i have no modules open so if
we close node modules then go down to our creator edit and
we tack above our form and inspect for post dot
assets we should see oh no
bump want did i forget my weight nope
oh oh it's all right dot first or fail there we go let's find the first post or
fail there we go
so there's our image you can see i don't have bite size alt
text credit or anything like that saving currently that's on the to-do list
alright so now that we have that coming through i need to
provide that to the component so we have the placeholder let's break
this down
let's do yeah assets as post.assets
and then within the file manager there's a javascript version in the edge version
we need to pass into this
why do i have posts in there
it doesn't make a whole lot of sense to have a poster right in there let's take a look at what the script's doing i don't
images yeah okay so i must have just gotten that wrong whenever i was okay
so it takes images and that passes it directly into there so
what does images need what's needed for images so we need id source
and then the underlying file is there and then whether or not it's loading
okay
so
we could map it and convert it into the format that we need it to be in here my question is
for file is that the actual
yeah it would have to be after the actual file and then source so then we would also need to
save whether or not it's a new image
or a new image that needs uploaded no no we wouldn't need to do that that's just if
a file is chosen now we need to upload an image once and the image is uploaded then we already have the id set
and it's just a matter of managing the id to the post so
all we should need to do is pre-populate this files only needed whenever you upload
beyond that it's pointless so
it should just be a matter of images dot
map and then mapping file name to source
source is image.file name um
loading is going to be false because it's already going to be there i think that should be it
uh if i was smart i would create a constructor function to just create a
new one of those that i don't have that object in multiple places but that that's fine that's fine for now and then
within here instead of this post we want images and then instead of an empty array we
want what am i passing this in is assets assets
and then just in case assets is not provided for whatever reason
um well i yeah you also need to support the create case so
we have it's very true so the same page is used for editing and
creating so we don't have this preload for assets um on the create because it's
no way it's going to exist so within if i can get back to the creator
edit whenever we try to create this is just going to bomb so we need to create
that as nullable because we may not have a post and then within here
we need to do a check so set assets equal to assets
or an empty array and then here i think this should be okay and then we
should just need to do we might need to stringify that or
something but we'll try this first we'll see see if that works it's been a while since i've passed something from
um edge into alpine i've just been using plain old view
so we'll see how this handles it um so we have an error we have
a list of errors image uploader object object yeah i had
a feeling um so
try json.stringify and see if that works first
okay all right um no error per say but we did get a 404 on
the image which makes sense i don't have that path
up well that's just not the right path at all um
so it's let's see it should be starting directly at the three right there's no it doesn't start with a
slash or anything like that i don't think right so
it would just be the internet seven starting at the seven three is the post so it's going from seven and onward
um so we need to either prefix that with
the route that we'll end up using for sharp or
we need to prefix that with the actual url to digitalocean
i would say in this use case we'd probably want it to be the direct url to digital ocean but
i don't know it would also be beneficial to not have it be full size like if that's
if that's a 3000 pixel image or whatever you know you don't need to load that in this little 100 width block
so let's go ahead use let's go ahead and get the actual url set up um
i do not remember what that is at all so i'm gonna just
routes so i think this is the one that i'm
using now because i specifically have the user id on there
i'll copy both of them just so that i have both of them now and then we can get rid of the one that's not in use anymore just in case
okay so go back to routes
and tack this one up here okay
oh i guess both of them would be yeah no no it should just be this one
because user all ids well we'll see
let's comment this one out and then we should now be able to take
this file name duplicate this page
and go to image
and get the image ta-da all right cool
and the reason that that's working is because we have our image and then all of our image paths will start with
the user id followed by the file name so then within our assets controller
there is the show method which
i think i showed earlier on which takes any active query parameters with you know any alterations that it should be
done on sharp if there aren't any then it just won't alter it
so if it's not cached and it's not an svg then it will check whether or not it actually exists using our storage
service so this is checking to make sure that it exists on the digital ocean if it does not exist
using this temp name which includes the temporary directory which is cache followed by the path which is
what tax on whether it should be looking
in i actually don't really know what that
does
oh okay it just joins the wild card together
the wild card route params together um so it's just building out the path
really and then it just takes the actual file name which is appended on any of the image
options that should be used with sharp um and then so if that
does not exist then it will try to get the altered image if it can get the altered image then it will upload that to that cache temporary
folder otherwise if there is not an image then
it will try to get the direct buffer from s3 if it's in this
svg it will use the actual path if it's not then it will use the temporary path
and then it will set set it as cached because
if it had reached that point then it would have been cached because this is what's
uploading the cached version right here and then it's depending on to the content type for this response the
format of the image and then it's actually just returning back the image itself
which is why we get the direct image from here so the first request took a second the
rest are nice and speedy because it has within the cache whether or not it needs to check for the file or it has within
the cache whether or not it is cached and doesn't actually need to check for the file first to see whether or not it
is cached so um all we need to do is prefix this with
that slash image so we can actually create
um just so that i don't like if that ever changes in the future for whatever reason um we can create a global
function to do that that way we just need to change it once in that global function as opposed to
everywhere so within here we have view already we
already have a global so we just need to view global
i mean we could use make route for this but i think it's going to be
let's call this image taking financing
inconsistent with that taken file name and return back slash image slash
file name and that really should be it so just have it called image and then within
our file manager we can
tell you what actually let's do a computer property on the asset
public get and we'll call this
asset url uh relative asset url is good
and then we will return back essentially the same thing that we just
did for that global function um so
uh but why don't we just move that off into the service and it will just use the service of both
so asset service everything in here is static so we'll
just do public static
get asset url take in
uh taking the file name sure as a string
and we will return slash image slash do the exact same
thing that we just did on the global variable and we'll go down to that and we will
do i need to import that instead of boot shouldn't need to
import asset service from
app services asset service
and then now we can replace this function with that function so asset service dot get
asset url give that a good save come back down to
our asset model and now we can do the same thing here so return asset service
get asset url and then this would just be this dot
file name that should be good then we should just be able to use asset url in place of
uh let's see in our javascript version in place of
file name since it's a computer property on the model it will be serialized so whenever we do json.stringify it will be here so
we should be able to do asset url give that a save come back to here and
bam there it is and then if i hit this delete button it's going to delete the file off of the
server and it's also going to delete it off of the image immediately i believe
we could test it wouldn't hurt so let's verify that is image with an idf5
make sure it's still there and if we hit delete boom it's gone
and if we come back into here and we refresh it's gone out of the database and if we come back into spaces
this file should be gone it should just be an empty local folder
bam cool we upload it again
there we go now we should have id of six yes
and we should have another image here
and there it is and if we refresh this page at this point in
time this image will still be here but it will be the one with the id of six um so
go ahead and test that oh i lied shoot
okay um
why is that gone it's definitely attached onto here
oh that's that didn't open up where i thought it would into the it's definitely attached on the database
it definitely exists um so why did that not populate
i had to do one more just for sanity's sake okay let's dig into that let's see why that
didn't populate because it definitely should have
let's go into the um start with the creator edit all right let's drop another inspect
just above our form
of our post dot asset assets
and it's an empty array so it's at the query level that things are going awry
all right so that would make me wonder we uploaded oh we uploaded it but we
never saved it so it never got saved to the post
that makes sense so that would just be a image getting stuck in limbo which
not fun but if we're creating them we don't necessarily have an id so and i definitely want them to create
automatically whenever you upload them because just reasons
so not the end of the world [Music]
but we could change that
to automatically bind to the image
so wherever that [Music]
file manager is tack on a post id on there of post
dot id and then within the file manager component
um no yeah yeah because we want to pass it
through [Music] post id of
post id really we could just pass through the whole post but
it's not really needed i don't imagine a case where we need anything besides the id so we'll leave
it as is and then within the javascript portion of this we need
post id on here as well
which will default which we don't need to default for because we want it to be undefined if it's not there um
then we'll add that into the no that doesn't doesn't need to be added into the state we can just use it
directly off of here so then if we go and look at the upload
there's the delete here's the get payload i think that's
where we would want it to be form data dot well we want this to be
inside of an f so let's give this some spaces so if
post id form data append
post oops post id of post id
so that should because here's the get payload call so then that should send up with this studio assets post to the api
endpoint um and i need to check whether or not i have any validation going on for that i don't think i have a class validation for it no i do not sorry
accidentally clicked so then within our assets controller on post
store sorry um we need to get whether or not there is a
post id so i guess i also need to put that try
catch back in at some point so const post id equals request dot
what is it only only uh
post id and then we that would be an object so we just extract that out of there there we go
um so if post id then after we create the
asset we would want to do
wheat asset dot related uh
yeah posts dot attach
that post id so
right yeah ideally we would check to verify that
this post id exists as well so that we don't get a form key error here
so
we'll bypass that for right now then let's just check and make sure that this actually works so i'm going to try to
let's go ahead and refresh for sanity's sake try to upload a different image
let's go with this one lla 502 let's open that
that took a while um so i'm selecting these images off of a nas server that i have here at my
house it's a server that runs off of my
wi-fi so it could be an issue with my wi-fi as for the reason why that took so long
uh looks like we got back at 500.
insert into asset posts null value and column post id
so click post id went up as null so maybe that's not
on the oh yeah let's see i absolutely hate firefox whenever it comes to
inspecting multi-part payloads it does not handle it well
does not look like it went up with the payload i did save it yeah
i yeah all right i guess a good starting point is to check whether or not it's there
like actually in this script um so whenever it's instantiated let's go
ahead and console.log post id okay
yep it's there all right so why would that not send up
with it it could be it could be the way that i'm grabbing it that's the actual issue
well and maybe i just didn't actually see it within the payload
um
i'm gonna go ahead and console.log out the form data here as well
before no that does already have the image on it let's do let's do this before the image and then
we'll console.log it out maybe that's why maybe it was after the image maybe it did actually send up and it was just after
okay and then
so we know that that one's working so we can get rid of that and then within our assets controller
on store see it could be the way that i'm getting it i might be misremembering
that portion right there
tell you what
let's debug it and see don't think i have my debug key in here
yet their script
okay and then i am not going to use fire well i can't use firefox for this
but i do not like firefox with debugger um so let's see i what i have edge
and then i need to restart my server using excuse me got something stuck in my throat
okay so i gotta restart my oop i'm gonna restart
my server npm run debug
go back to edge go back to localhost 33833
you need to sign in oh
bollocks
okay there we go
um so we'll go ahead get into that form
right and then let's inspect and then we'll open up the node debugger
verify that i actually put that debugger keyword in there yep cool so what i want to do is i want to
scope out one is post id actually something
two is it on this only
call or do i need to access it somewhere else i don't know if multi-part changes things i don't know
if having a manually processed route which this is
changes things that's why i'm not digging into the documentation for this per se but rather just going straight to
a debugger so let's go ahead try to upload an image here so
it's coming here
scroll down scroll down some more go to you and let's try you
all right let's open you up all right we're at our debugger point let me hit next so that it actually goes
to the proper place so post id is indeed undefined here
our request let's dig into you
so the body is empty and the data is empty parens are empty but that makes sense we
have multi-part data it could be in here
inside of fields fields looks empty
i really don't think it would be in files that would be the actual file
okay let's try
hmm i was just i'm already in request so i'm not gonna take the request again
um all right it doesn't really look like it
went up with it so
i must not have been seeing the uh formed data wrong it must not be sending
up but i don't know why it wouldn't be sending up it's in there it has a value
so i'm gonna go ahead and see i don't know if i can
i don't think chrome lets you inspect anything network wise until it resolves
but we'll try yeah yeah yeah no there it is that's
it's there
okay i wonder if i need to get it the exact same like request multi-part
form data or whatever that was that i was seeing in there
it's like i was saying this is a manually processed route um so
it's perfectly possible that adonis doesn't have any of the information with any of the data that was sent
in terms of like having it populated directly onto the request so
let me scope in on the multi part maybe it's in there
if it is in there i would imagine it would be on the fields but i'm not seeing it there
but then again i don't see the file on the files either but that's definitely there
so maybe it i don't know
well that does take a callback too though doesn't it
i could just make this easier on myself and pass it up as a param
uh but i would love to have the not need to define a separate route for
it
yeah i know i'm gonna take a look at the documentation real quick because i do have the manually processed
kind of mentioned a little bit in here um under the direct file uploads
so like that's that's managing the stream
oh gotcha not going to be available until after
the process that makes sense so i should have started with the documentation
okay let's continue on with that we'll accept the failure at that point we'll close out of this um
and close out of that close out and i'll undo that so
not available until after the process so we need to remove this what are they what what what are
they using input inputs query string in it
i'm not i don't remember i'm pretty sure that i have this right so we'll stick with this now since it's processed we should be
able to get it off of the request is what i'm getting at so we'll try that see if that works
um so we'll save that and we'll give that another go
i'm trying to think if i changed anything else that i need to undo i don't think so oh need to restart the
server back into watch mode so that actually picks up the changes
let me run dev there we go and i'm going to close edge there we go
all right i just saw you refresh but i'm going to do a manual refresh just for sanity sake
and all right let's try this again um this image would have been up there a
couple times by now so let's do nope definitely not a psd all right
let's do this one all right so let's open it up
all right we got back some errors and it was the yeah
is it still the same issue yeah no no
no this time it's on sword order cool so that means that we're getting the post id yeah
so that's that's working so now yeah um instead of updating it like that
we want to get the most recent sort for the post as well so
const post
asset max sort not the best name but let's roll with it awaits post
dot query dot where id
is post id and
no sorry not post undo's undo undo undo
um where uh so we want the intermediary table so we'll want to do database
from um what asset posts is what it's called
asset posts
okay and then all we need to do is order by the search
order oh well we also need to do the where right where um
post id is post id and then order by descending
of the sort order and then
first no we don't want to fail but we do
want a default
you know be a nice extension to this as first or default
um okay so wait zero default it to zero um
okay i think i have that right so we'll get the
max sort for the asset post table for the post id descending order of the sort order if
the one's not found actually we're going to be doing increment of one so let's do negative one that way it defaults to
zero so then we'll change this from an array
to an object with the
what is this the post id post id so that would be post id and
sort order of post asset max sort
plus one or we could just do plus plus okay it doesn't like that so we'll do
plus one and now that should save the sort order
along with the post so that should get rid of that error and
i might move that into a service because that kind of prolongs this already kind of
long function a little bit more um so this failed so we'll go ahead
uh i doubt that delete's gonna work but we'll try deleting it yeah i didn't think it would
what'd you fail on out of curiosity yeah i never saved into the database but it's saved up on the server okay
gotcha so we'll just refresh um we'll try this one more time
so all right let's move into one of the quick nope that was that was a quick tip
uh let's do one of the old thumbnails all right
all right yeah cool so that seems to have worked we got back at 200 um with the asset response
and it updated here okie dokie and if we refresh here we're
going to see us several images but we should see one in particular that is yeah the adonis 5 controller's
image that we just uploaded and now this file should be bound to the post without
saving so if we refresh it should still be there fingers crossed
all right cool cool and then if we delete it it should go away
and looks like it did should be gone from here should be gone from the database as well
actually i never even but yeah we can tell by the file name and it's not in there anymore um
and then if we refresh here it should have no issues and not show so
cool and now let's go ahead and before we wrap up let's go ahead and test multi files so
upload u um i think i have the yeah the uploaders just one at a time so we'll have to do
this one at a time but there's one um do you there's two
oh no ah
insert into asset posts invalid input syntax for type integer
object object oh yeah okay
so this isn't necessarily returning back just the number it's returning back the whole record
uh results [Music] but i think since this is a database module if we
just select a single column maybe it would return back just the number
let's see so we just want to get back to sort order
what post id is this three yeah
what is it what would it what i think it's a load db yeah load
db so we'll do a wait where's my code
await database from asset posts
there it is it's just db in here
dot where post id is three
order by sort order descending
dot select sort order
dot first right yep
okay cool so that does now just come back with a sort order but it's still with an object so i want to extract it
out of that object so we'll do
sort order as post asset max sort
there we go so now this should work
oh wait a minute the other the or needs to now be
yeah or be an object of sort order with negative one there we go all right so that's not very clean
but it should work i'll always clean it up later i always say that but boy does that
really ever happen um so if we reload we should still have this username sign in and post crud
thumbnail which we do and now if we select our second image
i will do this one awesome so that upload
uploaded as well we'll do a third one
and that one went as well so now if we check the database we should have three
images tied to the post oh this is just the asset table but we
have all three of those images there and if we check this table we should have three images
tied to the post with an id of three which we do and
without saving if we go ahead reload all three of those images should still be there which they are
and then we can save go back to edit and it should still
be there can't access property after k is
undefined um let me look up one more can't access property after k oh that
says the same thing okay
where are you getting k from it's definitely in the image loop
all right it's the one feedback i have from alpine
as their errors could use some work but apart from that it's a great package um
expression images so if i had to guess
images is undefined and that's what this k is
so why would images be undefined well we've tried refreshing
and everything worked but whenever we actually saved the asset ids
must not have sent up and we are calling sync so if they don't
send up then they get detached so my guess is
maybe they got detached oh no that's even worse they got
reattached okay am i calling attach instead of sync
on sync assets that would be a brain forward if i am let's see where is that that's in the
post service
yep call and attach that should be sync so the good news is
they're still sending up on the edit and they're saving on the edit the bad news is we have duplicate records now so um
let's see how that handles that so each of these have their own unique id the sort order
duplicated the asset id is duplicated i have to assume nothing's going to handle this so if we refresh we're probably
going to get six assets no we're still going to get an error
well that makes sense because we really didn't fix anything we just changed how it was stored
cannot access property after k is undefined so are we getting back our images at all are they coming back
let's go into our creator edit
and inspect post dot assets
yeah they're they're there okay so it's
probably a duplicate key issue if i had to guess maybe within that loop
um where is that key
right here oh no that's the image id
yeah no that still would be duplicate wouldn't it huh no yeah
yeah it would it would be it's that's not the that's not the intermediary table id that's
so i know well i don't know how to do index
in alpine i don't think it's provided as a secondary argument to the x4 i think it's
more like angular where it's just index uh
i don't know i'll try that and oh no it's not that okay
try the other person maybe it is more like view now i thought it was like angular
yep so it's an index issue um essentially duplicate ids
well that should never happen but now that we're calling sync anyway um
but without before we save that we can fix that by deleting oh no that deleted both of them
oh well um all right we'll we'll keep two we'll
just go go into the database and clean that up so i would imagine that only oh no that did okay
so let's see you and you you go away okay
because we still want some on here to test with and then now if we change this back to image id
should be okay and now if we go back to post them back in it should still be okay
yeah and now if we republish it should not have reduplicated itself
because we're calling sync but they should still be there yeah cool
so now if we come back and we should see the exact same thing as before awesome
last thing to check with this it does create still work or did i
completely break it so so far so good uh let's give it a title
and let's give it an image
yeah all right i bet you it's something to do with that
yep all right
so this is going to be within the assets controller
we need i thought i'd put that in a if but evidently i did not so
let's do if post id and that should do it
i only want to query the sort and only want to bind it to the post if there is an actual post id to bind to
so the alternative approach to that is to immediately create a post whenever you
hit new post and then have anything that you do within here just update so then this page will just
always be an update page and not a create page that's the alternative approach that i could do but for right
now i don't see this as being that big of an issue um i never really write in here anyway i
always just import so that won't be too big of an issue um so
let's go and try okay cool so we're back to working now
try another image just for sanity's sake make sure i'm not doing a psd
there we go okay and let's give it a title
and let's publish so actually
yeah okay so i don't need to publish it okay cool
so creation's working editing is working those could use a loading icon but
also they're not they're still playing showing the full size so that's the last thing that we could do
is tack on a file size to that uh actually so let's go
but where is that that's in the file manager
yeah so that do it inside of the template actually
um so on the end of the image
where's the source here's the source we will change that into
ternary they're not a ternary but uh this is just a string
that whatever that backticks are called it will tack on the query string of
width don't think that's quite one
what is your width 176. so we can just let's round up to
200. so we give it a save and now those images should come through
as actually 200 pixels wide
and voila 200 pixels so that cuts back on the amount that it actually needs to load and
they're a little bit pixelated um although i am on a high resolution
display so it should be more like 400 so all right let's change that to 400
i could just put a source uh image picture source with like the
multi whatever but not that big of a deal especially not in
the back end where seo is not an issue at all so there we go that's much more clear
and there should now be 400 pixels cool and so now if we go into
i don't know whether or not based off of looking at the code it should be on the root cache
nope it sure is not so it must be in local i must have that
scope in it somehow so cash there's my seven so now we should see
oh what what are those images um installing lucid and getting data by the
users i don't know what i called that one but if we go into i think it's this one
so if we go into this one now nope either that or it's not saving
right let's just say 11 minutes ago so undefined
that's not ideal one hour
ago all right well all right let's find so the other one was installing those those are ones
where i actually named it after the lesson so it's going to be one of these two uh
i'm gonna take a gander and say that that timestamp's bigger yeah so here we go
so i need to fix that where if it's not defined it doesn't come through but so
you can see this width undefined quality on the fine which should just not be there
is for the normal full size image and then we have width 400 which is the 400
width image cached then we have the width 200 which is the 200 image with cash so that's kind of how that cash is
working just to give you a visual of what i was talking about earlier apart from that i think that this is a
good stopping point for today we what we went on three hours today so a half hour longer than we have been
doing um but we got the images fully working for our post the other side of
the equation is to actually show them which we have already working we just need to actually implement it on the
front end side so yeah lots of fun stuff ahead lots of fun
stuff i think we covered today um hopefully it made sense i i know i just kind of scan through what i have with my
images all of this is up on the github which is down in the description linked down in the description
um so you can always check all this out as well i'll be pushing this up just after i end the
stream so thank you all for stopping by i appreciate your time hopefully you learned something hopefully this was
beneficial in some way if it was let me know what you learned and what you found beneficial about it i'd really appreciate it other apart
from that thank you for being here thank you for the support i'll see you next time
Join The Discussion! (0 Comments)
Please sign in or sign up for free to join in on the dicussion.
Be the first to Comment!