Rebuilding Jagr.Co, Social Auth & Image Management

In this stream, we work on finishing up social authentication using AdonisJS Ally. Then, we'll add project and permission settings and image uploading to Digital Ocean Spaces.

Published
Nov 20, 21
Duration
3h 5m

Developer, dog lover, and burrito eater. Currently teaching AdonisJS, a fully featured NodeJS framework, and running Adocasts where I post new lessons weekly. Professionally, I work with JavaScript, .Net C#, and SQL Server.

Adocasts

Burlington, KY

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.

0:02

in the queue um my dog kept bringing her bone to my feet

0:07

and chewing on it so then the audio was getting picked up so um yeah so we're going to be continuing

0:13

on with the uh jaeger co rebuild um so not much changed since the last stream i

0:18

did go through and kind of set up the image uploader um

0:23

it's not 100 done yet but so i created an edge component called file manager

0:29

that uses inertia to kind of create a javascript state that handles drag events drop events

0:37

change events and essentially allows for the upload of multiple images

0:43

and then that is just used in the create or edit i don't have it actually

0:49

persisting onto the post yet and i don't have since i

0:55

don't have it persisting to the post i don't have it actually here it is

1:02

setting existing images onto it so it would reset even if they were saving onto the

1:10

onto the post so that's really all that i changed um and i did ver verify that

1:16

the social authentication um issue that we were having was because

1:22

of the i got the um i i got the environment variable wrong for that so i got either

1:29

the secret or one of the other ones wrong on it so it actually does work now actually i

1:34

think i already have my user created yeah so

1:40

you can see here's the user um and the username actually comes through

1:45

as my full name instead of an actual like username like this would be so that's what that

1:52

sluggification was for so i need to go through add that back in but

1:58

let me delete that user and show you that that is actually working real quick so we'll delete

2:03

me and come back to and i never did fix the scroll issue my bad i completely forgot about that oh

2:10

cool okay my work around's still in place so that works um so yeah go ahead hit the g button

2:18

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

2:24

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

2:31

see there's my user so i need to delete that yet again but you can see that whenever you

2:37

authenticate via social auth you don't have a password but you do have access tokens

2:42

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

2:48

ahead and get the username set up um and then we can go ahead and test the uh

2:54

github authentication as well so i actually forgot to do my pre-check

3:00

work and find a package to use for this so let's go find a package um

3:08

i'm sure javascript would work just fine but we'll go with whatever so this uh the the second result was the

3:15

one that i was using before that i was getting import issues with so we'll try this one i looked i don't

3:21

believe the adonisjs slugify that we're using for the post

3:28

no that's on the model that we're using for the post slug right here uh exports its logification

3:36

sluggify method i don't know why i'm calling it solidification so that's why i'm looking for a

3:41

different package because this time we're going to be doing it specifically inside of a service before something's saved

3:47

and it needs to overwrite any existing value so i could manually bend the

3:52

adonisjs slugify package to do that but i think it would just be easier to find a

3:57

separate package to do it so this one looks fine let's go ahead

4:02

try to find what its package name is probably just yeah sluggify okay

4:09

so we'll try this one

4:18

okay and we'll go ahead where was that that was in

4:24

oh i also cleaned up the services so you know how i created an http context

4:29

service so i renamed that to base http service and i moved all of the ones that

4:34

are going to be extending that inside which currently is just auth social service inside of an http folder within services

4:42

that way it's kind of structured similar to the controllers right so any service that's going to be explicitly for http

4:48

requests i'm going to put within services http and it will extend this page base http service that then

4:56

provides the http context to that service via this so all these other ones i think i just have

5:03

as static functions at this point in time so yeah and then this is the asset service

5:08

so now this is something else that's new as well

5:14

um i think all this does is apply sharp alterations which i don't have in place

5:20

yet so that's something that we might be getting around to either this stream or the next stream um so essentially i have

5:25

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

5:32

if i inspect this image you'll see it's doing image and then

5:37

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

5:45

is really i need to change that that's really big for that slot um

5:50

but it's also got source images um altering it to web p altering it to

5:57

different sizes um actually it's probably using this 550 right here

6:03

so essentially i can tack on any other width and

6:10

this service will then take that and apply a sharp alteration to it

6:15

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

6:24

show here there we go i was previously using node cache but i'm going to switch this to redis

6:31

but essentially it will cache it it will it will save whether or not the image is cached

6:36

the cache will actually save out onto my spaces i use the digital ocean spaces to save everything

6:43

and it will save that altered version so that cache is literally just saying whether or not a

6:49

cache version of that image exists on spaces hopefully that makes sense uh we'll get into that probably

6:55

i i doubt it'll be at the end of this stream but it'll probably be in the next one um

7:01

but yeah as i was saying let's go ahead and finish up the social auth

7:06

so social ah service what was i gonna do oh sluggify right so just import

7:11

slugify from sluggify that was a default export yeah

7:18

yeah okay and then down at the bottom here i had this commented out because i felt like i might need it

7:24

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

7:30

any of the arguments to that here in a second and then i also need to import the database module as well

7:36

and then we'll just do a like to determine whether or not you will get what the latest

7:42

incrementation of the particular username is so like if

7:47

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

7:53

with github which uses a different email it would then you know save as a different user

7:58

but my name is the same so it would try to save tom gorbach as the username again this will check for that

8:05

and if tom gorbach already exists as the sluggified username it will tack on the

8:10

occurrence length to the end of it similar to what the adonisjs slugify package is doing just this time we're

8:17

doing it inside of our own service function so now all we need to do is oh look at

8:23

that even had this just commented out so await this dot get user name

8:30

and then actually you use this so it should just be a matter of that i

8:36

think why is that red username exists oh it's it's voidable

8:43

are you nullable oh no it's oh i'm not returning okay well that would help

8:50

return actually i can just instead of setting resetting username i'll just return it

8:55

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

9:03

yeah okay so head back in here we'll come back to sign up

9:09

we'll try google again okay expecting one binding saw two

9:18

i'm gonna assume that's something going wrong with that sluggification

9:23

expected one binding saw two

9:30

pardon my dog um okay

9:37

so let's make sure that that's actually just returning back a string

9:45

and then we'll see what we get here as well

9:50

so we'll go back try this again see what we got the consoles

9:58

um yeah so we got the username so it must be on that db query so i think i said this in the live

10:05

stream but this was previously specifically for my sql so i don't know

10:12

i'm very used to my sql not all that used to postgres which is what i'm using now so

10:19

i i feel like it might be something with that query let me try taking let me try altering it

10:25

real quick just to see so we'll do const currencies equals

10:39

dot and then we'll just do where is there yeah we could do like just a

10:44

simple where like to test this out so where

10:49

user name is like

10:54

and then

11:00

let's do well so our alterations only on the end

11:06

so we would only want the like uh wildcard to be on the end and then we'll tack username right in

11:13

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

11:24

yeah okay there we go and so the slugify did not lowercase it i would

11:30

prefer lowercase but that's not the end of the world we'll see if that's an option real quick i guess

11:35

oh yeah lower um replacement remove remove characters that match rejects

11:40

defaults to undefined trim

11:45

okay yeah let's go ahead and set lower to true um

11:51

well i don't know that's not the end of the world though yeah and we'll

11:57

there yeah we'll keep it for now keep it as this and what the heck i'll go ahead you see

12:03

how i'm decisive i am with this sure lower is true um and i'll go ahead

12:10

and just keep this for right now it's probably not exactly what i'm looking for

12:15

but i will do a search later on to find what the postgres version of that r like is

12:22

okay so we have our google version working

12:29

let's verify whether or not github is working

12:34

cool github's working and you can see the username would have come back the exact same

12:40

i actually need to do a unique check to make sure that because i want that to be

12:47

unique cased as well so that would come down on the validator

12:53

so nope that's post store so the actual validator for this is in line so it would be within the auth controller

13:00

and actually it doesn't hit that does it

13:07

nevertheless that unique check also needs to be uh

13:14

case insensitive so let's see

13:21

that would i guess it would be right on my query where i previously had this right

13:27

so actually since we're lower casing it to

13:33

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

13:38

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

13:44

tom there you go so we'll sign out we'll sign back up

13:50

with google cool all right so you can see now it's tom goodbridge with a one at the end

13:57

because now it needs to be unique we already have a time governance in there so it needs to increment itself

14:03

so that was that that's working good um and then you can see the avatar url's

14:08

coming in with this as well so why don't we go ahead and make use of that and then we can go

14:15

ahead and tack a gravatar default on the one that's not um

14:22

using social auth excuse me

14:28

so see we can normalize this within the model by using a computed property

14:40

sorry i need to do a bigger cuff um so let's go down i like to do these

14:46

before all of the um relationships and all that do computed

14:52

what i think this these are public get if i remember right um and then that was saved in databases

15:00

avatar url so i think i'll just name this avatar and then let's also install gravatar

15:11

i think that's the package name okay and

15:16

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

15:22

specific way just spell that right yeah

15:28

okay so we'll do if this

15:34

avatar url then we just return the avatar url otherwise we need to get

15:40

the gravatar um

15:54

no i'm not using it is that it i didn't specify node nope that's it

16:00

okay so it looks like it's just gravity url

16:06

and you pass an email along with any options that you need um

16:12

okay simple enough otherwise return gravatar.url

16:18

and then we would do this dot email and

16:24

pass in i'm gonna assume s is the

16:31

size so be the square size that's probably probably probably 40

16:38

it'll be okay it's not that big um so we'll give that a save and then we'll head into the

16:44

header is it just header i think it's just a header and we need to find it

16:57

nope that's not quite it here here's the off check so it would be

17:02

this right here so instead of doing that we should just be able to do

17:09

off user and then avatar

17:15

so give it a save let's see if it works yeah okay cool so there is my gmail

17:22

avatar it's really old um and then we'll sign in with and i did

17:27

not apply that fix here but let's see if i can get rid of devtools if i can reach it

17:32

all right yep there's another really old picture of me and then we will try our gravatar user

17:44

and there is our gravatar i'm i'm sure there's like like the specific show

17:49

different image than just the gravatar logo but that works for now all right so all of

17:54

that seems to be working okay um let's go ahead and

18:01

so right now we can be logged out and still head into

18:07

the studio posts if i can spell it right we can head into there um

18:13

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

18:21

it you need to be authenticated so let's go ahead and add that in

18:26

i'm gonna minify all this stuff get it out of the way for right now

18:32

and see do we already have a middleware i cannot remember whether or not it comes with a off middleware it does

18:39

all right so our redirect 2 is not quite sign or log in

18:44

but it's sign sign in so we'll change that and

18:50

everything else should be okay so we'll head into our routes

18:56

if i was smart i put all of this inside of a studio yeah

19:02

this will attack a middleware onto this it's going to need more than off so i'll

19:07

put it inside of an array for right now [Music] and then since

19:13

yeah okay i commented it out here i think just for testing but we'll need it on the assets api as well

19:21

so we'll give that a save [Music]

19:32

and unable to connect oh yeah okay i gotta add it

19:40

thank you error so we'll go into the kernel i'm gonna add it into a named one

19:45

what am i using today is off i'm using this off import

19:52

app middleware off okay

20:02

uh royalman royman sorry i'm horrible with names um

20:09

it says can you please tell me how to handle file uploads and requests in adonis js so

20:15

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

20:21

was just easiest to copy over what i had last and then work on upload updating to

20:26

using adonisjs drive at a later point in time um i'm handling it 100 manually so i'm

20:32

actually reading the streams so you can see i've got request multi-part on file and i'm grabbing the

20:37

file via its name of image and then i'm getting the extension

20:42

creating a custom file name for it my byte size doesn't work i never

20:47

bothered with figuring that out because it's not that big of a deal to me and then i'm uploading it using my

20:53

storage service i know that this is probably not the in-depth

20:58

level that you're looking for but then i have this storage service

21:05

which points to what directory within my storage i want this to be saved to

21:10

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

21:17

then anything in production saves to public [Music]

21:22

and then i'm using digitalocean spaces which is very similar to just using the aws sdk

21:28

so i'm actually using the aws sdk to upload so everything here is essentially the

21:34

same as you would use to use the aws sdk but if you're not

21:40

looking for that level of control

21:46

there is a library adonis has it's a first party library called drive

21:53

i would heavily recommend going through this documentation page um

21:58

it makes files very easy to handle um

22:03

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

22:09

i'm doing um so i would definitely recommend this approach especially if you're just getting into file uploading

22:14

and it it supports s3 like i said i i'd love to get my code over to using this

22:20

in the future because it just makes things every it makes things entirely more readable than what i have so

22:26

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

22:33

um did i ever fix this yes okay

22:39

so now if we try to go to studio

22:46

and posts we should be redirected right back to the sign in which we are

22:51

and then if we actually sign in and now we go to studio posts

22:59

we're allowed so that works a-okay and that should be for the entirety of

23:04

studio which at this point i think we just have the posts right

23:10

yeah okay yeah these aren't even go on actual pages or just go on the hashtags so

23:17

um all right so that was easy what else do we have uh let's see let's take a look

23:24

at my list so social authentication not completely done we still need to add

23:30

in the profiles i meant to before this stream go through and

23:35

finish up all of the models and migrations i was i didn't want to

23:41

bore you guys with porting all that over um but we'll see

23:47

and then add auth and real check to studio yeah so actually roll so

23:53

the way i have it right now is if you're an administrator you see the dashboard you see posts series topics and comments

24:00

and then if you are not an administrator you see settings and maybe something else maybe you do

24:06

get dashboard but dashboard was never completed so i always i always save dashboards for

24:13

last um it's just a habit of mine i guess because well you

24:19

need stuff to actually put in it before you can actually do it but then i just never get around to it so

24:25

um yeah so let's go ahead and do that so if

24:31

you're an admin you should be able to see post series topics

24:38

you know maybe comments was public too because then if somebody replied to your comment it was visible there

24:44

so we'll just do post series and topics if you're an admin for right now so

24:54

let's do a new middleware

25:03

and we'll call this and we'll do it unspecific to a roll

25:08

we'll specify the role whenever we define the middleware so i don't want to just name it admin um

25:16

studio access no no no no it needs to be a rule

25:22

sure it works for now it's better than ever thinking uh so

25:28

let's see let's go ahead this is going to be a named one so we'll go into our kernel

25:34

roll import app

25:40

middleware and roll and then [Music]

25:47

um let's see did the arguments come in directly in here i don't 100 remember i

25:52

think they do no that's next that wouldn't be it

25:58

hmm i'm gonna look don't mind me i'm gonna look

26:05

see if it's in the documentation real quick middleware middleware middleware

26:31

okay okay that makes sense it was the next one so right

26:36

guards passing name one time yeah that's it cards

26:44

which is a string array yes okay

26:50

we're gonna make that required um

26:56

so okay

27:02

what we need to do is i have my rolls as an enum

27:09

and what we need is the authenticated user so we'll do off

27:15

so we'll do const auth role id equals off user

27:23

role id if

27:29

yeah well then we need to get the guard role id um

27:34

let's see rolls

27:41

yeah so we'll do

27:58

object.keys match it to the key

28:04

is it roll just roll that

28:11

did not seem to import from anywhere let me go and just import it directly so import

28:16

name it rolling on from app enums

28:22

roles rolling on dots yeah no just rolling

28:28

them so there's the those will be the keys

28:35

so that will work um and then we will map it

28:45

to lower case okay and then we will do

28:52

well no that's not the guard roll keys that's the that's just the roll keys and then we'll

28:58

do const guard keys equals

29:05

guards dot map

29:13

and then if

29:21

i have a tendency to overthink things whenever i'm trying to also talk through it

29:28

objects possibly undefined it's fine

29:35

so really what we want is not just the roll keys that match it against

29:42

the guard keys we want the roll key that the authenticated user has

29:47

so const off roll key equals roll

29:53

keys what [Laughter]

29:58

okay i'm overthinking this the const just a simple search time

30:06

cost off roll key equals object dot

30:12

keys no rolling on roll

30:19

a num dot find

30:29

key.2 lowercase equals but no you need the id

30:36

so we need values of that which then this is a number

30:44

so we'll do id id equals auth roll id

30:51

in which case we will return back yeah that just worked that that'll work fine um

31:00

authoral id oh no that is duplicate so instead of doing this we'll do off user

31:08

rule id we'll get rid of that okay

31:13

is that even pointful why am i doing that i'm sorry

31:19

const

31:59

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

32:04

the authenticated user has and you want to get the key for that

32:21

well why not just turn these into the ids that would be easier tom come on all right so you have you

32:29

have the guards so then all you need to do

32:34

is return back a map of roll enum

32:41

and then that is that yeah k.2 uppercase

32:49

that works for the roles that i have so that's not the guard keys but the guard ids

32:55

guard roll ids and then if off user

33:03

roll id i need to do includes so if

33:11

guard rule ids includes

33:16

oh my gosh finally got it sorry that took me so long mental lapse

33:21

uh and then we need to do the negative so if not then we need to throw

33:28

an exception my goodness so uh

33:50

yeah well yeah we'll make an exception for that so what that'd be like not authorized um

34:03

not allowed what's that i don't quite remember the verbiage for the http code

34:19

uh four oh [Music] unauthorized yeah okay 401

34:26

so

34:34

there we go i'm not delete not authorized i was close

34:40

oh no exceptions all right and

34:46

goodbye okay and then

34:54

i don't quite remember what i usually do for these off the top of my head let me go look

35:03

so i know i already have one i think i just pass it to the super

35:12

oh so here i called it not allowed yeah okay so i just pass it into the super

35:17

key easy enough oh that was for a 403 though so we'll do okay and then e

35:24

unauthorized there might already be an adonis

35:38

and then all that we need to do is whenever we want to throw it just pass it in a message so

35:47

here throw new

35:53

unauthorized exception

36:00

yeah i'm gonna just put a default message in place so that we can just provide it like that because uh for

36:06

the most case that's gonna just need to be the same thing you're not

36:12

authorized to perform this action okay

36:19

there we go all right took me forever but i finally figured out what i needed to do for that

36:26

and then let's go ahead and down in did i already add it yeah okay so roll is what i called it so

36:33

now we just need to apply it to just the

36:39

specific studio here studio so posts so we'll just tack it on to

36:45

each group as it's applicable so that would be post a series and topics so do middleware

36:51

um roll and then what is it is it colon or is it

36:58

just comma gold on commas all right so then this

37:04

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

37:10

admins so we'll also need to do that

37:15

where's the role id oh here it is are they all admins what's one

37:21

one's most likely basic just a normal old user where's my enums

37:30

yes one is user two is admin so we'll go ahead and make google the admin

37:36

since that'll actually be the account i'll end up using so there we go

37:42

and so i'm logged in currently as

37:50

i don't remember so we'll just sign back in uh we'll try test user one first

37:58

good old test user to test things out so we'll also need to hide these since they're no longer applicable but

38:04

whenever we go to posts there we go there's our e unauthorized you're not authorized to perform this action

38:10

so so far so good let's go back let's go ahead and test google

38:18

okay now we should be able to see posts oh no

38:25

i did update it right i did push the update i might not have let's try that again

38:34

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

38:42

there um and then for sanity's sake we'll test github as well

38:52

and there we go okay cool so let's go back to google and we'll verify that we can still

38:58

no i don't want to sign up i'm going to sign in that we can still create posts and all

39:05

that stuff so we could see the create post form um if we publish we should get back an

39:12

error which we do if we create one with the verbiage of testing

39:17

all right cool so everything seems to be working okay delete did not delete so i guess that's the

39:23

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

39:29

so i must have that or it could be something with the admin check but i doubt it

39:36

um that wouldn't just break that so let's go take a look and see why that delete's not working

39:43

so that would be what views studio

39:48

index all right so we have a four method of posts

39:54

and that's providing the actual method as delete posting to

40:00

studio post destroy with an id of scope row id

40:13

well off the top of my head that looks right so

40:20

let's i suppose see if

40:39

so let's head into that delete call and let's see whether or not we're actually even just getting

40:44

there if we're not getting there and then we'll try to remove the middleware if we are getting there then

40:50

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

40:56

params as well we'll just go ahead and try to knock two birds with one stone

41:02

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

41:08

blocked by some form of a middle oh you know what did i ever put the csrf token in that form

41:13

that would be a good reason why it would stop working randomly

41:18

i did not so there you go csrf field

41:24

all right and so i don't have any toast system to

41:31

notif send notifications quite yet so i don't really have any way to tell myself exactly what the error is there

41:37

but all right let me go back and back

41:44

okay there we go all right so yeah now it seems to be something actually with the post so it

41:50

might have been something that i broke whenever i um

41:55

probably something with a route so let me take a look at the underlying

42:00

route that that delete call has and see what the final one actually generates out is

42:07

well yeah that would be an issue wouldn't it it's just a form

42:13

it's got no submit so either i put something in here wrong or

42:21

i'm having a oh wait a minute what's that extra form block doing there

42:28

that could cause some issues all right let's give this another go

42:34

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

42:42

okay

42:48

cool so that is a happy edit it was just a stupid issue that i had with that um

42:54

let's see what next so social auth is working

43:00

uh we added an off and roll check we need to actually finish that roll check so

43:06

um because if i sign out sign in and

43:14

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

43:21

access to here currently post is the only one that actually links anywhere but

43:27

ideally that shouldn't be showing and then if i actually go into well none of these other pages actually

43:33

but if so let's go ahead and just add studio or

43:40

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

43:46

that little studio header i also need to hide it there so we'll add settings in just so i can actually

43:52

see that with an authenticated user unauthorized user i mean

44:00

so nope outside of posts so we'll do route group

44:08

prefix um settings as

44:13

settings uh we already have the off middleware because it's in the outer group

44:20

so then we'll just do a route.get for the index page for right now and do i have a controller for this

44:26

no so we'll need to create a settings controller as well

44:33

and we'll just do that okay let's go and create that controller really quick

44:48

okay oh i meant oh well we'll just drag and drop it

44:53

forgot to prefix it with studio um and then oh and i forgot to mark it as resourceful

44:59

that's all right it's not all that resourceful public async

45:05

index

45:15

okay and we need view

45:23

and we'll put this at studio

45:29

settings index yeah that'll work for now you can always change it so

45:34

that should all be good now we just need to stub that page i'm just going to copy the post index

45:41

and then we'll just delete the table so studio

45:46

settings index edge

45:54

oh i copied the file not the contents of the file okay well that doesn't help any

45:59

all right and then we need to get rid of that table

46:04

so really that's just all of the content minus the negative margin uh which is going to get rid of that as

46:10

well and text settings on there and that button needs to go away

46:18

i don't know whether or not that will have any button and then the heading will change from posts

46:24

to settings okay so now with our non-admin user

46:32

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

46:38

header did i put these up to the um yeah okay

46:45

nope that's comments settings so route studio

46:51

settings index save missing token

46:58

okay so now we should be able to go down to settings and yeah so ideally

47:04

since this is not admin user we want to hide post series and topics and just have it be dashboard comments and

47:09

settings so

47:14

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

47:21

the user is a admin admin however we can do that at a more global

47:27

place so that we can we don't have to actually perform that check in each place that we need to do it

47:36

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

47:42

that approach because i know i didn't have the best approach for it with the old project

47:50

but it doesn't hurt to actually look and see what i was doing actually i think i just placed a config

47:58

did i did i did i nope oh wait settings

48:08

nope not not quite okay

48:14

well i don't really remember what the heck i was doing with him i could always just do something in the

48:20

provider for it

48:30

do something like user settings why don't we try that approach

48:38

there's always that adonis package i've yet to use it but it does

48:45

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

48:52

but i'm gonna try something i'm gonna try putting like a settings within

48:59

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

49:06

and then also pass it as a global to the view [Music]

49:11

so services this will very much so be an http service and

49:18

we'll call this um settings service sounds sounds

49:27

appropriate for right now so export default

49:32

setting service extends base http service

49:48

i mean you cannot find name setting surface oh i always do that forget the call to class

49:54

okay and then um

50:01

so we'll do i guess just getters off of this would make sense so we'll do get

50:07

permissions um

50:14

and then we will just return back a list of permissions so

50:21

there well

50:27

yeah let's change this from yeah

50:32

so then we'll do access

50:38

um let's see what's the best nesting for

50:43

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

50:49

something so studio yeah that's definitely too robust because you would be doing settings

50:54

permissions access studio can

51:00

all right so let's just kick it back one let's get rid of access so studio

51:05

can posts

51:10

and then we'll have um public get

51:16

is admin right and this will just return back a boolean whether or not the current user

51:23

is an admin which we can take out of

51:28

the role oh no no we can't actually um i forgot that we didn't just do it

51:35

admin middleware um

51:41

so then we will just do return this

51:48

ctx off well no i think i just put the user directly off of that right

51:56

i didn't are you sure i thought i did

52:01

yeah get user right there

52:10

oh no that would just be this.user not this ctx yeah there we go okay

52:15

this user dot role id equals

52:23

import really numb from app hems

52:28

roles really numb dot admin

52:34

all right and then um

52:42

yeah i'll go ahead and i don't want to explicitly make that so

52:48

well yeah we can make that yeah let that make it safe

52:53

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

53:01

this that is admin and if we add any other roles we can you know

53:06

simplify the checks by adding a additional is statement up here so can access posts can

53:13

access series

53:20

and then we can also do can manage posts

53:29

can manage series

53:34

can access topics

53:40

can manage topics

53:47

all right i'm gonna put a break in between each set there we go um

53:57

and then we will do another one actually i'll put this back off of

54:03

here get

54:10

is authenticated return this user

54:17

wow butchered that return there we go return this user and

54:23

okay there is a specific i think is authenticated check um on the off

54:31

but that'll work for right now um can

54:36

access dashboard there's really nothing to manage on the dashboard whenever we actually get around to actually building

54:42

it so we won't have a manage permission

54:48

so you can access that if you're just authenticated

54:54

and then what's next comments

55:02

and this one you will be able to manage comments because they'll be bound specifically to you

55:10

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

55:16

and then last is settings so well really these are pointless right

55:22

they would just be true but because uh we have the off middleware on it but we'll

55:29

check that again here just i i don't know for safety's sake i guess can access settings

55:37

can yeah yeah sure

55:44

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

55:50

non-studio stuff in here as well and then we'll have other middleware

55:56

checking whether or not you're an actual owner or admin of a particular post series

56:02

or whatever um for what since i'm the only one that posts to jaeger right now

56:09

that is not very necessary because i'll be the only one that ever has access to the

56:14

post series and topics but um back whenever i had the multi tenancy

56:20

and other people could post and all that stuff um that was very much a useful thing

56:26

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

56:32

but we'll worry about that later on in the development so now we just need to utilize these

56:39

and actually create the provider for it so within our

56:45

provider here we can either well it wouldn't make sense to register it as a binding we can put it on our http

56:52

context if we want i don't know if that fully makes sense

57:00

for right now we'll just leave it as a service but it would be good for the view to have access to it

57:09

yeah we'll go [Music] yeah we'll go ahead and put it on the http context what the heck

57:19

um let's see so that uses

57:30

yeah okay so we will do

57:38

construct settings service

57:44

equals import

57:51

amp services

57:56

http settings service

58:01

and then we will do

58:08

i'm blanking

58:29

see this is a real reason why i make these lessons so that i can refer back to no i'm kidding um

58:36

let's see so this is a group

58:47

did i title this one wrong

58:54

i want to add a global property to your http oh no here it is just got to scroll down further down

59:00

so

59:09

ah gotcha

59:18

okay i gotta do it in a middleware

59:28

good old brain farts all right so node ace make

59:35

middleware um oh what would be a good name for this

59:50

there we go just call this app state

59:55

and then within our

1:00:02

kernel will make this a global

1:00:09

middleware

1:00:15

actually i think i think i have this on my previous

1:00:20

project as well middleware app state so

1:00:27

here we're going to want uh the request no the route

1:00:33

route um well we're going to want to just change this to ctx because we're going to want

1:00:39

to place something on it so ctx okay

1:00:46

if

1:00:52

ctx dot route dot

1:01:04

no request dot

1:01:09

method equals and then

1:01:16

i'm trying to think yeah let's just put this one on all of it so

1:01:25

import setting service oh thank you

1:01:32

and then we will do const settings service equals new

1:01:39

setting service no that does not need that um

1:01:45

and actually we'll just do ctx dot settings

1:01:52

equals new setting service and then we need to create a new contract actually extend that and then we'll have other things in

1:01:58

here as well something specific to get requests um

1:02:04

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

1:02:10

setting service middleware um so contracts

1:02:16

i do not already have http context in here so we'll create that

1:02:22

no not s

1:02:27

there we go declare module

1:02:43

http context contract and then

1:02:51

settings how do i set

1:02:56

this to an instance of a service

1:03:22

no http settings service okay

1:03:31

i doubt that'll work but yeah

1:03:37

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

1:03:43

think if i set it like this then it would be looking for static properties on this right

1:03:51

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

1:03:57

this instance of it um i don't quite know how to do that with typescript

1:04:07

so we'll try this we'll see if this works so all right so if i do that it seems happy

1:04:12

with it it seems not to be complaining but

1:04:19

yeah there's permissions there's okay yeah okay

1:04:25

yeah so that seems to be working um

1:04:30

cool so then all we need to do is

1:04:40

it into

1:05:15

okay i'm not 100 percent enough that'll work normally anytime that i end up view

1:05:21

global i'd do it within a provider but

1:05:27

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

1:05:34

that works so um within our header then we'll go ahead and do that drop down

1:05:39

first so we'll do

1:05:44

um

1:05:50

show i guess

1:05:56

yeah that'll work and here then we would want to do settings

1:06:01

studio can

1:06:07

access dashboard start with the dashboard we'll see whether or not that works or throws in

1:06:13

there and then we'll just tack on a show true for the rest of these real quick

1:06:32

okay and then we will apply this

1:06:38

if we can find it right here okay so

1:06:43

really just wrap this up in an if so if route dot show

1:06:50

okay probably even better just to filter the studio routes

1:06:56

when i think about it rather than actually tagging an if on it let's see if

1:07:06

dot filter um r dot show so we'll see if that works

1:07:16

cannot read properties of undefined reading can't access dashboards so studio is undefined there um

1:07:24

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

1:07:29

is set at all or whether that's something else entirely uh so i will

1:07:34

replace cut this out replace it with just true for right now

1:07:41

and then drop a drop and inspect here

1:07:49

settings and we'll see what we get

1:07:58

oh no well i was not expecting the whole http

1:08:04

context

1:08:17

why would that be happening

1:08:24

crew

1:08:50

look and make sure i'm actually setting that right so view views and templates

1:09:20

that does allow an airplane sorry about that

1:09:28

actually it's probably in the reference no no no it wouldn't be that's that's

1:09:34

for that's for um actual usage this would this would be more like the

1:09:39

view module um well tell you what actually let me go

1:09:54

i know i have a ton of them on this project so i'll just poke in here real quick

1:10:01

and all of these are set on the provider yeah yeah so i should be able to just

1:10:07

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

1:10:14

know why that would set like why that would come through as the whole http context as you can see here you got ctx

1:10:22

let me try const

1:10:28

settings equals new settings service

1:10:36

ctx settings equals settings and then instead of setting it off of the ctx let's just do

1:10:43

settings no it's no different

1:10:50

[Music]

1:10:56

all right let's see whether or not we can actually find settings on here then uh it's circular ah

1:11:04

oh um yes that uh that's a very good point

1:11:12

it is extending the base http service which

1:11:19

provides this ctx so that is very much so circular so we don't want to do that

1:11:24

at all

1:11:31

so instead this would probably be better as a builder or something of the sort that doesn't return back necessarily

1:11:38

itself but something scoped within itself um

1:11:49

so the public want damn yeah no no don't do public um async

1:11:57

no it wouldn't need to be async so do public provide a build method on here

1:12:04

and what this will do is it will just return a cherry picked portion

1:12:10

of its settings um that way that http context doesn't get carried through on and it's not

1:12:15

circular so that this will work for right now this is a good workaround for right now um so we'll do

1:12:29

just cherry pick some of the things off of it

1:12:39

all right not exactly a builder but

1:12:46

it'll differ so app state then then instead of doing this we would want to

1:12:51

instantiate it and then call build yep in which case that's going to

1:12:59

complain because the type no longer matches so raises a whole other question and

1:13:05

probably just define a separate interface for it right

1:13:13

yeah i would i would think so

1:13:20

because i don't think that you can do can you

1:13:34

i'm spitballing here i don't know if that's valid doesn't look like it a const initializer

1:13:41

is an ambient context

1:13:48

okay yeah

1:13:53

so i don't think that is valid so we will do another i can either add it in as a contract

1:14:00

where i can put it directly in the service probably just do it as a contract i guess

1:14:05

let's see so um settings.yes

1:14:21

and then what um oh nope not not you

1:14:27

you no not you where is it

1:14:35

so sitting service there you are all right um

1:14:43

so we'll probably need to define a type for permissions so that we

1:14:49

can specify all of these booleans uh or we could grab that type right off of

1:14:55

am not the strongest with typescript i have not spent a whole lot of time devoted into learning it

1:15:01

um so if i'm doing something that is

1:15:07

absolutely way too over the top just to get something done here let me know in the comments i'd love to

1:15:13

know so let's see so settings would be then is

1:15:19

admin which is a boolean is authenticated which is also a boolean

1:15:26

and then permissions

1:15:33

which again i don't know how to get that instance of

1:15:39

settings maybe i can do um

1:16:12

yeah no okay where's the shot

1:16:26

can i copy and paste that tooltip that showed up before that has all that defined for me already

1:16:32

copy it and pick yeah all right cool let's save some time

1:16:38

don't like that that means that that's a manual list but i don't know how to go about doing that automatically

1:16:43

at least not off top of my head without doing some digging so then this would just be

1:16:49

permissions and then on our http context instead of doing that we would want to i guess

1:16:57

do i need to import that contract import

1:17:02

see but neither of those were default but we would just need um

1:17:07

settings from

1:17:15

contracts settings is that that good yeah and then that becomes type of settings

1:17:22

yeah okay and then we need to check our app state and it's still red

1:17:31

types of property permissions are incompatible

1:17:37

all right it's inside of the studio that's so okay it's back down to

1:17:44

settings so

1:17:51

this would really be yeah i don't know can i do an object with

1:17:56

a probably but it would probably be easier to just do

1:18:09

that

1:18:16

okay are we are we clear now good

1:18:21

all right so that was a big

1:18:27

loop all right so um back into what is it our

1:18:32

header yeah let's verify that now our settings are our settings and not the http context

1:18:41

so i'll probably give it a read yeah all right cool there it is

1:18:47

and so actually we can see i should be able to access well no all of this is just defaulted oh no these

1:18:53

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

1:19:00

of them then we can access and manage comments and settings so there we go

1:19:08

i'll leave that inspect there for a second and we'll just go ahead and apply these so that would be

1:19:13

settings studio can access

1:19:19

dashboard then this one will be settings studio

1:19:27

can't access posts

1:19:34

can't access series

1:19:41

can't access topics

1:19:52

you can access comments and finally

1:20:02

can access settings so we'll give that a save

1:20:08

oh man i was so confident in it

1:20:16

okay let's undo back to all trues verify for a second

1:20:22

here permissions permission studio okay

1:20:30

so it should be settings permissions so we'll do uh find and replace for settings dot

1:20:38

one two three four five six seven yeah okay

1:20:43

and then just remind me to fix that route because that's going to get matched too

1:20:49

so replace that with settings dot permissions dot

1:20:55

not as short as i would love it to be

1:21:01

but it's okay for right now we could also add in a getter as well so

1:21:06

we could do like settings dot get can access settings

1:21:13

um or settings that studio get can access settings or something like that but

1:21:20

this will work for right now can access comments i must have named that something different

1:21:30

oh no i didn't spell studio right there we go

1:21:36

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

1:21:42

the same thing here as well so i'm going to go ahead and get rid of

1:21:47

that inspect now it served its purpose and i'll probably go through

1:21:53

some point later in a week and clean up the long key that we need here just to check out

1:21:58

permission make that a little bit shorter so i think that's in the settings layout

1:22:04

or studio layout oh no header studio okay

1:22:14

okay and

1:22:19

good enough so now i just need to replace this true with that setting strip

1:22:56

okay

1:23:04

appearance is turned off off so we'll skip that

1:23:13

that's not even in the current code base that was in the code base before it but carrying it through

1:23:19

so back when people other people were allowed to post there was it never quite made it out to production

1:23:25

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

1:23:31

your grays and all that fun stuff so um that's what the setting the appearance page was for

1:23:37

um okay so that should now be good at well no well eh

1:23:44

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

1:23:50

rr.show active no show yeah

1:23:55

okay so there we go so it's kind of minified but well not minified but you

1:24:01

know smaller dashboard comments and settings is all i can see so now if i log out

1:24:08

and sign in with google we should now see all of them so there's yep dashboard posts if we go to settings

1:24:15

we can still see all of them and we still access posts as well

1:24:28

okay so what next should we tackle

1:24:36

we got often roll checking done for studio

1:24:44

um well why don't we

1:24:51

start with binding the image

1:24:56

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

1:25:02

pre-populating this image selector with any um already

1:25:08

images already attached onto the post so we'll try to take a guess at it first

1:25:14

before actually testing it so let's see here

1:25:19

let's take a look at first what i have the file manager actually attaching the

1:25:26

image as like the name

1:25:32

asset ids okay and it's an array of them so

1:25:39

on our post validator we need to ensure that we have asset ids

1:25:46

somewhere in here i guess also i need to verify that

1:25:51

that's one of the tables that i ported over yeah asset posts and assets are right

1:25:58

there okay so

1:26:09

schema.array i guess that would be optional because if you don't provide anything it should

1:26:14

be all right do i have this double defined why are you giving me a red squiggly here

1:26:20

remember so yeah okay

1:26:33

are you taking away where is that go look real quick i don't fully

1:26:39

remember what the array validator type takes validator array

1:26:47

yeah i didn't think it would need a whole object okay

1:26:55

dot number number number and then um

1:27:02

we would also want to do a rule check to make sure that the actual id exists that we're passing in for each

1:27:09

one of these so we'll do rules dot exists

1:27:15

tables assets and column is id

1:27:20

okay so now it should be coming through on our validator

1:27:28

then on our posts controller

1:27:34

see we'll want to do store first i suppose

1:27:41

um instead of tagging it in with data and the like we would want to do asset

1:27:49

ids extract that out of the data and then

1:27:55

where we're also attaching in our posts we'd want to essentially do the same thing with the ids so we'll await

1:28:01

auth user related assets oh did i not

1:28:07

define the relationship here oh no this is from the user sorry

1:28:13

await post dot related assets there we go

1:28:19

dot attach and then this should already be an array of our

1:28:24

ids um

1:28:32

but we have intermediary data i don't remember if

1:28:39

it's yes i do need to buy a license for you um

1:28:47

sort order i don't remember whether or not that has a default value actually i can check real quick structure

1:28:54

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

1:29:01

that um so

1:29:07

we need to create that structure and once we create it then we'll extract it out into a service because it's not

1:29:12

going to be the prettiest looking thing so um

1:29:20

do const assets sure

1:29:25

equals asset ids dot

1:29:31

and then we'll want to reduce it um

1:29:40

all right something in the middle and we'll start you off as an array

1:29:46

so and then we also need the index that's the third and then reduce right yeah

1:29:54

so uh essentially what we want to do here is on

1:30:00

we want to return

1:30:07

and then we want to tack on the current as

1:30:13

trying to decide how much of this uh see so we want to do we're going to be doing it via the post

1:30:20

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

1:30:28

be as it's defined in the database yeah it doesn't hurt to define it that way not that much

1:30:34

so we'll do that and that will just be directly the current so this would be better

1:30:39

labeled as current id so we'll do current

1:30:46

id and then the sort order is what i call it right

1:30:53

uh yes sort order as just

1:30:59

i so it'll start at zero and then as it loops through it it will increment it as

1:31:04

needed um yeah so that should be

1:31:10

all we need for the assets and then we will do a

1:31:15

post related assets

1:31:21

not sync for saving so detach would be pointless but we will attach

1:31:29

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

1:31:34

happy about here um asset id sort order

1:31:41

array of undefined is not assignable to parameter of type string or number array

1:31:59

let me go take a look at what i um

1:32:08

i thought it was just um no it is an object isn't it it's the

1:32:16

object the id is the key uh yeah so

1:32:21

i need to go take a look at that remind myself how that works so database down to

1:32:27

many to many should be something like save pivot

1:32:37

attach maybe maybe it's an attach yeah okay

1:32:49

yeah so not quite an array but rather an object where the key is the um okay so

1:32:55

let me get rid of that const asset

1:33:01

data equals um asset ids dot reduce

1:33:09

previous current id and index same as before

1:33:16

and then instead of starting with an array we'll start with an object and then we will return

1:33:24

a spread of previous and we will add on the current id

1:33:30

and that will have does that need to be

1:33:36

well i'll define it as is defined in the database sort order of our i

1:33:44

think that should be it um we can actually clean that up a little

1:33:50

bit by getting rid of the i don't know whether or not that would

1:33:55

actually be cleaning it up just making it even messier but uh we'll try

1:34:04

that let's see yeah it's not horrible

1:34:09

okay and then instead of assets here it would be

1:34:14

asset data and it's still red what am i getting wrong now

1:34:20

argument of type object or undefined is not assignable to a parameter of type

1:34:25

string number

1:34:36

should be an object though no it is an array isn't it no no no it should be an object to get

1:34:43

an object right right

1:35:07

we're going to try this out okay so let me copy this

1:35:13

i save that as asset ids right yeah oh man i fat fingered it

1:35:26

varian says uh i've been using this framework for about five months in development scheduled to go to

1:35:31

production in january sorry for my english is good um that's awesome

1:35:37

hopefully you have a smooth transition transition to production whenever you do launch

1:35:44

um i guess i need to type this all back out

1:35:50

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

1:35:57

okay const asset ids equals array of one two and three

1:36:03

okay const asset data equals asset

1:36:09

ids dot reduce previous

1:36:15

current id and i and then we have returning an object

1:36:20

if i hit enter is this going to damn yeah

1:36:27

all right let's try that again nope shift enter doesn't do it okay well let's just keep typing them um

1:36:36

prev otherwise current id

1:36:42

as an object with sort order

1:36:47

of i and then

1:36:53

hopefully i got that right

1:36:58

did uh did it work i don't think it did otherwise we would

1:37:05

have gotten undefined back wouldn't wave

1:37:17

let's see if it saved asset data is an empty object

1:37:24

okay

1:37:36

all right well

1:37:43

maybe i am misremembering how to use reduce

1:38:09

where's my post service there it is so in here i think i have a function called sync circle sync posts and sync

1:38:15

series and all that stuff so i'm just gonna cheat and

1:38:21

sync assets ids reduce

1:38:27

yeah that's fairly similar to what i'm doing already

1:38:32

maybe i just need to call sync i don't know [Music]

1:38:37

i am doing some checking to make sure that the uh array is an actual array

1:38:44

i'm going to copy this and just compare it and see how different it

1:38:51

is so reduce previous

1:38:57

term pre yeah that's that's the same

1:39:04

so it could just be visual studio code not quite cooperating with me it could be that

1:39:09

this needs to be sync

1:39:16

let's try sync

1:39:30

i'm going to try manually defining something in there and seeing whether or not i get the red squiggly there too

1:39:36

so let's change that back to attach so i've got a feeling it's just visual

1:39:42

studio code maybe let's do one with a

1:39:51

nope nope that's very okay it must be something i i don't know

1:39:56

that's strange

1:40:05

well i mean that's trying to return back an empty object or undefined

1:40:19

i'm gonna cheat see here so post service

1:40:28

oh passed it sync uh nope did not quite pass it sync assets right here

1:40:34

and okay yeah so we need to get rid of that undefined is what it looks like so

1:40:40

it's visual studio code tagging oop that's the wrong app attacking on that x or question mark i

1:40:47

think causing the issue so if we change that to an exclamation yep okay that is indeed

1:40:53

surprised my head didn't go there

1:40:59

so then we would want that to be within our validator to either be required

1:41:07

or we could default it to an array if it's not provided um something like that

1:41:13

i think we'll go with that approach because the assets aren't required so we'll go

1:41:19

with that approach okay so that should work for the store then we just need to do the same thing for the update and

1:41:24

instead of copying and pasting that directly um again we will move that out into a

1:41:30

service which i don't think i have a post service

1:41:35

quite yet so we'll go ahead and create one of those

1:41:49

okay

1:41:57

and just so that it can be used for both the um

1:42:02

store and the update i'm going to instead of calling attach for storing

1:42:08

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

1:42:15

we want both of these so we'll want to cut that out completely

1:42:21

and sync assets we'll define what it takes here in a second

1:42:26

so first and foremost it's going to need the post and it's going to need the asset ids um

1:42:32

so i would say take the post first and then take the asset ids

1:42:40

which is where we can now define this as an array of numbers or

1:42:46

default it to an empty array so we'll do number array equals empty array in case

1:42:53

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

1:43:00

and then that should be it i think right yes

1:43:06

okay then we just need to call it so a couple of ways that we can get this

1:43:12

into our post controller we can either manually define it on the constructor

1:43:18

or we can use the ioc container to inject it so do object there and then we will stub our

1:43:27

constructor public posts

1:43:32

post service type post service and there we go so now our post

1:43:40

service is available via this so we do await this dot post service dot

1:43:46

sync assets post and asset ids

1:43:53

this could also very well have been a

1:43:59

static like nothing in here requires http context so

1:44:05

on second thought

1:44:11

let's get rid of that let's make you static

1:44:17

i i can't immediately think of anything that we're going to need a post service for

1:44:22

that cannot be static or that wouldn't be overly drawn out being static

1:44:29

so to keep things clean let's actually just go ahead and make this static and then since we are no longer using

1:44:36

http for this let's move it out

1:44:41

okay and then we no longer need the bind for this and it'll no longer be on this it'll just be accessible

1:44:47

so we no longer need the constructor so pardon me for that little circle there um so now instead of this post service

1:44:53

it's just a wait post service i don't want to make things more difficult than it need to be

1:45:00

so let's go with this approach and then down here on the updates after

1:45:06

we save let's go ahead and do the same thing so await

1:45:11

post service dot sync assets and we actually need to extract this out of the validation data

1:45:18

as well so asset ids oop there we go

1:45:26

this takes the post and the asset ids so

1:45:31

and then lastly whenever we delete a post we also want to delete any of its

1:45:37

assets so

1:45:44

let's see here i can't think of an instance

1:45:51

where we would because images are going to be tightly woven to the post like

1:45:56

i don't have an image library where the post will be able to share assets so let's just completely delete

1:46:03

the image whenever you delete the post too so that comes into the storage service i

1:46:09

think i have a delete or destroy function on this that actually destroys the

1:46:16

actual thing so we'll want to call that so we'll want to import that as well

1:46:25

import storage so uh does that take the file name

1:46:34

yeah okay so we'll need to query

1:46:43

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

1:46:49

const assets equals post.related

1:46:55

assets dot

1:47:01

query dot

1:47:07

nah well yeah yeah yeah that'll be all right

1:47:12

um we could have just loaded it but that that that way the assets are directly in here and then

1:47:22

select trying to think if i can both select and delete but i think that would be

1:47:27

counter-intuitive so we'll select just the

1:47:36

file name i think i have that all under case lowercase

1:47:42

yeah um so file name is all that we want

1:47:49

and then asset

1:47:58

file names and then we'll also do await post

1:48:04

related assets query dot

1:48:11

detach and then delete well all right let's go ahead and select

1:48:17

the ids then too let's see

1:48:23

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

1:48:29

delete if you're using cascades then that's really not that's a one step solution um

1:48:38

so then we'll take those ids so we'll just change this back to assets

1:48:43

and we'll do let's see

1:48:51

ids equals assets dot map oh whoops i forgot to put the weight on

1:48:58

the front of them

1:49:05

okay so now we have an array of just the ids so we're going to await because we've detached before we deleted

1:49:12

so now we can't do related to get the related posts that we need to delete so we'll do awaits um

1:49:22

asset query wherein

1:49:29

asset ids delete so that oh no what do i got going on here

1:49:38

oh yeah no i need to specify that those are for the id we could do file names as well but

1:49:46

just in case we'll do it specifically on the ids um

1:49:51

and then we also want to delete the actual asset file so we have the ids here

1:50:01

let's also do const so file names

1:50:14

okay and then at least i don't think i have a storage service solution that takes

1:50:19

multiple it's just one at a time and i don't know whether or not

1:50:26

the s3 sdk has like delete objects where it takes an array

1:50:40

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

1:50:46

so i don't have to loop over and call this destroy

1:50:55

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

1:51:00

file capital n names um so it'll be a string array

1:51:07

taking the same arguments from thereafter directory

1:51:12

return back a promise of a boolean just stating whether or not they were successfully deleted

1:51:18

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

1:51:25

any unused assets and purge them so that's it's no biggie if there is an issue here but

1:51:31

ideally it should work okay and then

1:51:37

directory i don't know why i'm not just copying and pasting this from right above but

1:51:42

whatever actually that's gonna need to yeah

1:51:48

let me take a look and see so destroy objects

1:51:53

what do you take so this disc which is the s3

1:52:00

sdk instance um delete

1:52:06

objects all right let's take a look at the other

1:52:12

usage delete objects request

1:52:21

so i need to figure out what all that takes see if i can inspect this

1:52:27

peak

1:52:32

um not gonna tell me let's try clicking into it

1:52:39

oh no that's the output no i don't want the output shoot

1:52:46

peak definition

1:52:51

so we want the other usage where's the other usage right above it yeah there you go delete

1:52:58

object request which takes the bucket which i believe we have

1:53:03

um

1:53:11

bucket name delete all right so delete's got to be an object containing like the file names and all

1:53:17

that right i'll just open it be right next to it all right let's go into delete so i

1:53:23

could have just so objects i'm not gonna remember this we gotta

1:53:28

all right so it takes what bucket first right

1:53:35

delete what was that something delete request to delete object request

1:53:42

objects request yeah bucket and then delete so bucket

1:53:49

delete um

1:53:57

and then objects um i don't know why i would need to do that

1:54:03

quietly so we'll do object

1:54:10

i keep sorry i keep wanting to switch app objects

1:54:16

which is what another object object identifier list which then should

1:54:22

be just the file names right

1:54:28

object identifier key version id so yeah that's where i

1:54:33

had would have then just the keys so this

1:54:39

see if we can would be

1:54:57

no we won't need index

1:55:02

prev and then

1:55:08

no this is an array right objects is an array it would have to be

1:55:15

all right let's start back up at the top delete object request

1:55:21

come on click click it there you go delete objects

1:55:27

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

1:55:33

keyword for for each of these that doesn't make any sense so instead of reducing here

1:55:39

this would be an array so filenames.map file name if i can spell it

1:55:48

and then an object with capital k key as

1:55:54

file name then i think that should be it then we

1:55:59

should be able to use it the same as just delete object up above up above

1:56:05

so it would be params dots promise to turn it into a promise

1:56:13

then and then i was giving sublime text a test run

1:56:19

which is why i didn't know that that red squiggly was there

1:56:26

but oh well i know that that works so it's no biggie

1:56:32

i'm just gonna ignore it for right now okay

1:56:42

directory oh oh yeah okay so we need to do this for the key not just file name

1:56:51

all right cool so that should be okay so now we should be able to call destroy all

1:56:56

and pass it in an array of the file names so

1:57:02

await don't really need to await this there's no reason to it's not something that's

1:57:09

necessarily required so we'll just do this synchronous or not

1:57:14

synchronously but we won't make the request wait for it so we'll do storage

1:57:20

service dot destroy all

1:57:26

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

1:57:33

so that should work um okay

1:57:38

so now whenever this is all for destroying a post remember so

1:57:44

now let's go ahead and well that's not all that bad but to give this section of code a

1:57:51

particular name let's move this out into the

1:57:56

service so looks like all we would need is the post

1:58:06

so post service public static async

1:58:16

well it's doing more than detaching

1:58:25

destroy assets that yep take the post of type post and i think that would be

1:58:30

it because then we're finding the asset off of the post need to import asset itself

1:58:38

and we need to import stored service and that should be it

1:58:44

and then on the flip side we will do await

1:58:51

post service destroy assets and we need to await this

1:58:57

here because we cannot delete the post up until the actual assets are deleted and

1:59:03

detached from the post itself otherwise we'll get a foreign key issue um

1:59:09

and then we need to pass out the post okay

1:59:15

looks good let me get a drink

1:59:28

okay shall we give it a test run so i'm gonna

1:59:34

go ahead

1:59:39

if i can spell try to get in the digital ocean here if it doesn't make me open up my email

1:59:49

cool let's go into my space not the site but my yaeger space

1:59:57

so as i was mentioning like with the sharp alterations so whenever

2:00:02

an alteration is done um that alteration version is stored in this dot cache file

2:00:09

so all of these are alterate altered images

2:00:15

oh other folders so the image itself so that's the image file name

2:00:21

and then inside of it are all of the specific alterations

2:00:28

so that's how that's being done so these are all so like all the alterations for this

2:00:34

image are within this folder and you can see i've this is one of the earlier lessons so

2:00:40

it's gone through a couple of iterations of the site so

2:00:46

and then it would absolutely not hurt anything to just delete this cache folder should anything ever get too big

2:00:51

and then any images still in use next time that they're requested the new cache version

2:00:56

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

2:01:02

inside of local and then public i think uses that one no no it looks like it has

2:01:08

its own so i've changed a couple of things over

2:01:13

the course of time and i haven't gone through and purged this because i still have plenty of space left in this thing

2:01:18

so um but yeah so let's go into local

2:01:24

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

2:01:31

actually so let's go back out to here because uh

2:01:36

i need to be an admin now to save stuff which is my gmail so that user does not have an id of one

2:01:44

but rather uh two or three so let's go ahead refresh make sure that

2:01:50

we're still logged in and everything looks like the avatar broke

2:01:57

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

2:02:04

not right now anyway um so let's save a post so we'll call this

2:02:10

just testing image store

2:02:16

we'll give it an image uh select one of my other old thumbnails

2:02:23

okay we'll do we'll do this one all right so we'll select that so now

2:02:30

that loading indicator was just saying that it's still uploading but now that that's gone it's done uploading so we

2:02:36

now have an id populated within our um

2:02:44

image ids so if i were to do

2:02:50

document forms i think i called this post form yeah

2:02:56

and we do assets actually i don't know how that handles array

2:03:01

fields with this

2:03:10

that would be i'm not seeing it

2:03:15

let's see if title's in there yeah titles in there

2:03:20

so maybe i did not give that a name let's see

2:03:27

here's the input name

2:03:32

is yeah no it's empty so that's well no that's the that's the uploader

2:03:39

nothing's actually stored on that once the image uploads it's then moved

2:03:44

into this loop which would then have

2:03:51

yeah a name of asset ids and the value of 5 right there so

2:03:59

all right let's try just a straight up query selector so document.query selector

2:04:07

input name equals

2:04:18

it's been a minute since i've worked with array-based form fields

2:04:25

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

2:04:33

um so on our store

2:04:38

let's just do a quick console.log of the asset id see whether or not we're actually getting it back

2:04:44

um fingers crossed that we don't run into an issue with the validator actually

2:04:50

just to make sure i'm going to provide future dated publish ad because i think

2:04:55

i was dumb and i made this required so but just in case we'll provide it

2:05:03

so let's hit it and okay created

2:05:08

did we get console logged out of our asset ids we did awesome did it store

2:05:14

onto so we should have an asset with an id of five we do my user id is seven oh that's right

2:05:21

i've been deleting users so that makes sense so on our space we should now have a folder called seven

2:05:27

with a file name of uh 2021 lb jager011 and then a timestamp

2:05:35

on our asset posts there it is asset with ninety of five

2:05:41

post with ninety of three sort order of zero so if we come back into digitalocean

2:05:49

refresh there appears to be no refresh button

2:05:55

so i'll refresh the page there is our seven folder there is

2:06:03

our file so ql

2:06:08

um now the big thing to do is to alter it to where whenever we go back to actually

2:06:14

edit this we need to pre-populate that so

2:06:20

first things first we need to query it on the edit page show so we can get rid

2:06:26

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

2:06:35

as for the where's the edit here it is so now instead of just doing a finder

2:06:41

fail it would be beneficial to

2:06:46

preload some data onto it now we can either preload it or we can load it in separate um

2:06:55

not doesn't really matter it just changes how you reference things on the front

2:07:01

end so i'm going to go ahead and change this from a finder fail to a query

2:07:07

so we'll do const post equals 08 post.query

2:07:13

where id is params id

2:07:19

preload i'm going to break this down

2:07:28

assets and then at some point in time i will be

2:07:33

putting sortable on this so the order does matter so i want to do query

2:07:39

query dot order by da

2:07:44

sort order and we want that to be ascending actually

2:07:52

stick with that and then anything else i don't think so

2:08:00

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

2:08:06

problem with that actually using an index would be beneficial because then as we update the sort that would

2:08:13

update as well so um okay so now we should have the image

2:08:19

passed through within the actual form so if we oh i have no modules open so if

2:08:24

we close node modules then go down to our creator edit and

2:08:31

we tack above our form and inspect for post dot

2:08:38

assets we should see oh no

2:08:48

bump want did i forget my weight nope

2:08:55

oh oh it's all right dot first or fail there we go let's find the first post or

2:09:01

fail there we go

2:09:07

so there's our image you can see i don't have bite size alt

2:09:12

text credit or anything like that saving currently that's on the to-do list

2:09:19

alright so now that we have that coming through i need to

2:09:25

provide that to the component so we have the placeholder let's break

2:09:32

this down

2:09:41

let's do yeah assets as post.assets

2:09:48

and then within the file manager there's a javascript version in the edge version

2:09:55

we need to pass into this

2:10:02

why do i have posts in there

2:10:09

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

2:10:21

images yeah okay so i must have just gotten that wrong whenever i was okay

2:10:27

so it takes images and that passes it directly into there so

2:10:32

what does images need what's needed for images so we need id source

2:10:39

and then the underlying file is there and then whether or not it's loading

2:10:45

okay

2:10:54

so

2:11:01

we could map it and convert it into the format that we need it to be in here my question is

2:11:10

for file is that the actual

2:11:16

yeah it would have to be after the actual file and then source so then we would also need to

2:11:22

save whether or not it's a new image

2:11:29

or a new image that needs uploaded no no we wouldn't need to do that that's just if

2:11:35

a file is chosen now we need to upload an image once and the image is uploaded then we already have the id set

2:11:41

and it's just a matter of managing the id to the post so

2:11:46

all we should need to do is pre-populate this files only needed whenever you upload

2:11:53

beyond that it's pointless so

2:11:58

it should just be a matter of images dot

2:12:04

map and then mapping file name to source

2:12:18

source is image.file name um

2:12:28

loading is going to be false because it's already going to be there i think that should be it

2:12:34

uh if i was smart i would create a constructor function to just create a

2:12:40

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

2:12:46

within here instead of this post we want images and then instead of an empty array we

2:12:53

want what am i passing this in is assets assets

2:12:59

and then just in case assets is not provided for whatever reason

2:13:05

um well i yeah you also need to support the create case so

2:13:10

we have it's very true so the same page is used for editing and

2:13:18

creating so we don't have this preload for assets um on the create because it's

2:13:25

no way it's going to exist so within if i can get back to the creator

2:13:32

edit whenever we try to create this is just going to bomb so we need to create

2:13:37

that as nullable because we may not have a post and then within here

2:13:43

we need to do a check so set assets equal to assets

2:13:48

or an empty array and then here i think this should be okay and then we

2:13:54

should just need to do we might need to stringify that or

2:14:00

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

2:14:07

um edge into alpine i've just been using plain old view

2:14:12

so we'll see how this handles it um so we have an error we have

2:14:19

a list of errors image uploader object object yeah i had

2:14:24

a feeling um so

2:14:36

try json.stringify and see if that works first

2:14:44

okay all right um no error per say but we did get a 404 on

2:14:49

the image which makes sense i don't have that path

2:14:54

up well that's just not the right path at all um

2:15:01

so it's let's see it should be starting directly at the three right there's no it doesn't start with a

2:15:07

slash or anything like that i don't think right so

2:15:12

it would just be the internet seven starting at the seven three is the post so it's going from seven and onward

2:15:20

um so we need to either prefix that with

2:15:25

the route that we'll end up using for sharp or

2:15:31

we need to prefix that with the actual url to digitalocean

2:15:39

i would say in this use case we'd probably want it to be the direct url to digital ocean but

2:15:47

i don't know it would also be beneficial to not have it be full size like if that's

2:15:55

if that's a 3000 pixel image or whatever you know you don't need to load that in this little 100 width block

2:16:03

so let's go ahead use let's go ahead and get the actual url set up um

2:16:10

i do not remember what that is at all so i'm gonna just

2:16:15

routes so i think this is the one that i'm

2:16:20

using now because i specifically have the user id on there

2:16:26

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

2:16:34

okay so go back to routes

2:16:40

and tack this one up here okay

2:16:51

oh i guess both of them would be yeah no no it should just be this one

2:16:56

because user all ids well we'll see

2:17:02

let's comment this one out and then we should now be able to take

2:17:09

this file name duplicate this page

2:17:17

and go to image

2:17:23

and get the image ta-da all right cool

2:17:29

and the reason that that's working is because we have our image and then all of our image paths will start with

2:17:35

the user id followed by the file name so then within our assets controller

2:17:43

there is the show method which

2:17:49

i think i showed earlier on which takes any active query parameters with you know any alterations that it should be

2:17:55

done on sharp if there aren't any then it just won't alter it

2:18:00

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

2:18:06

service so this is checking to make sure that it exists on the digital ocean if it does not exist

2:18:13

using this temp name which includes the temporary directory which is cache followed by the path which is

2:18:22

what tax on whether it should be looking

2:18:27

in i actually don't really know what that

2:18:33

does

2:18:41

oh okay it just joins the wild card together

2:18:47

the wild card route params together um so it's just building out the path

2:18:53

really and then it just takes the actual file name which is appended on any of the image

2:18:58

options that should be used with sharp um and then so if that

2:19:05

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

2:19:13

folder otherwise if there is not an image then

2:19:19

it will try to get the direct buffer from s3 if it's in this

2:19:24

svg it will use the actual path if it's not then it will use the temporary path

2:19:31

and then it will set set it as cached because

2:19:38

if it had reached that point then it would have been cached because this is what's

2:19:45

uploading the cached version right here and then it's depending on to the content type for this response the

2:19:52

format of the image and then it's actually just returning back the image itself

2:19:57

which is why we get the direct image from here so the first request took a second the

2:20:04

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

2:20:11

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

2:20:17

is cached so um all we need to do is prefix this with

2:20:24

that slash image so we can actually create

2:20:32

um just so that i don't like if that ever changes in the future for whatever reason um we can create a global

2:20:38

function to do that that way we just need to change it once in that global function as opposed to

2:20:44

everywhere so within here we have view already we

2:20:51

already have a global so we just need to view global

2:21:00

i mean we could use make route for this but i think it's going to be

2:21:07

let's call this image taking financing

2:21:13

inconsistent with that taken file name and return back slash image slash

2:21:20

file name and that really should be it so just have it called image and then within

2:21:27

our file manager we can

2:21:40

tell you what actually let's do a computer property on the asset

2:21:54

public get and we'll call this

2:22:00

asset url uh relative asset url is good

2:22:06

and then we will return back essentially the same thing that we just

2:22:12

did for that global function um so

2:22:18

uh but why don't we just move that off into the service and it will just use the service of both

2:22:23

so asset service everything in here is static so we'll

2:22:29

just do public static

2:22:34

get asset url take in

2:22:40

uh taking the file name sure as a string

2:22:47

and we will return slash image slash do the exact same

2:22:53

thing that we just did on the global variable and we'll go down to that and we will

2:23:01

do i need to import that instead of boot shouldn't need to

2:23:08

import asset service from

2:23:16

app services asset service

2:23:23

and then now we can replace this function with that function so asset service dot get

2:23:32

asset url give that a good save come back down to

2:23:37

our asset model and now we can do the same thing here so return asset service

2:23:42

get asset url and then this would just be this dot

2:23:48

file name that should be good then we should just be able to use asset url in place of

2:23:59

uh let's see in our javascript version in place of

2:24:04

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

2:24:11

we should be able to do asset url give that a save come back to here and

2:24:16

bam there it is and then if i hit this delete button it's going to delete the file off of the

2:24:23

server and it's also going to delete it off of the image immediately i believe

2:24:33

we could test it wouldn't hurt so let's verify that is image with an idf5

2:24:40

make sure it's still there and if we hit delete boom it's gone

2:24:47

and if we come back into here and we refresh it's gone out of the database and if we come back into spaces

2:24:55

this file should be gone it should just be an empty local folder

2:25:02

bam cool we upload it again

2:25:11

there we go now we should have id of six yes

2:25:17

and we should have another image here

2:25:27

and there it is and if we refresh this page at this point in

2:25:32

time this image will still be here but it will be the one with the id of six um so

2:25:38

go ahead and test that oh i lied shoot

2:25:45

okay um

2:25:50

why is that gone it's definitely attached onto here

2:25:57

oh that's that didn't open up where i thought it would into the it's definitely attached on the database

2:26:07

it definitely exists um so why did that not populate

2:26:21

i had to do one more just for sanity's sake okay let's dig into that let's see why that

2:26:26

didn't populate because it definitely should have

2:26:35

let's go into the um start with the creator edit all right let's drop another inspect

2:26:42

just above our form

2:26:48

of our post dot asset assets

2:26:55

and it's an empty array so it's at the query level that things are going awry

2:27:00

all right so that would make me wonder we uploaded oh we uploaded it but we

2:27:07

never saved it so it never got saved to the post

2:27:14

that makes sense so that would just be a image getting stuck in limbo which

2:27:20

not fun but if we're creating them we don't necessarily have an id so and i definitely want them to create

2:27:26

automatically whenever you upload them because just reasons

2:27:34

so not the end of the world [Music]

2:27:40

but we could change that

2:27:46

to automatically bind to the image

2:27:51

so wherever that [Music]

2:27:56

file manager is tack on a post id on there of post

2:28:03

dot id and then within the file manager component

2:28:09

um no yeah yeah because we want to pass it

2:28:15

through [Music] post id of

2:28:22

post id really we could just pass through the whole post but

2:28:28

it's not really needed i don't imagine a case where we need anything besides the id so we'll leave

2:28:35

it as is and then within the javascript portion of this we need

2:28:41

post id on here as well

2:28:47

which will default which we don't need to default for because we want it to be undefined if it's not there um

2:28:57

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

2:29:02

directly off of here so then if we go and look at the upload

2:29:08

there's the delete here's the get payload i think that's

2:29:13

where we would want it to be form data dot well we want this to be

2:29:20

inside of an f so let's give this some spaces so if

2:29:25

post id form data append

2:29:32

post oops post id of post id

2:29:39

so that should because here's the get payload call so then that should send up with this studio assets post to the api

2:29:46

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

2:29:53

accidentally clicked so then within our assets controller on post

2:30:01

store sorry um we need to get whether or not there is a

2:30:07

post id so i guess i also need to put that try

2:30:13

catch back in at some point so const post id equals request dot

2:30:20

what is it only only uh

2:30:28

post id and then we that would be an object so we just extract that out of there there we go

2:30:33

um so if post id then after we create the

2:30:40

asset we would want to do

2:30:46

wheat asset dot related uh

2:30:52

yeah posts dot attach

2:30:59

that post id so

2:31:05

right yeah ideally we would check to verify that

2:31:11

this post id exists as well so that we don't get a form key error here

2:31:24

so

2:31:37

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

2:31:43

let's go ahead and refresh for sanity's sake try to upload a different image

2:31:49

let's go with this one lla 502 let's open that

2:32:00

that took a while um so i'm selecting these images off of a nas server that i have here at my

2:32:06

house it's a server that runs off of my

2:32:12

wi-fi so it could be an issue with my wi-fi as for the reason why that took so long

2:32:19

uh looks like we got back at 500.

2:32:24

insert into asset posts null value and column post id

2:32:30

so click post id went up as null so maybe that's not

2:32:35

on the oh yeah let's see i absolutely hate firefox whenever it comes to

2:32:41

inspecting multi-part payloads it does not handle it well

2:32:56

does not look like it went up with the payload i did save it yeah

2:33:05

i yeah all right i guess a good starting point is to check whether or not it's there

2:33:12

like actually in this script um so whenever it's instantiated let's go

2:33:17

ahead and console.log post id okay

2:33:24

yep it's there all right so why would that not send up

2:33:30

with it it could be it could be the way that i'm grabbing it that's the actual issue

2:33:36

well and maybe i just didn't actually see it within the payload

2:33:42

um

2:33:53

i'm gonna go ahead and console.log out the form data here as well

2:33:59

before no that does already have the image on it let's do let's do this before the image and then

2:34:04

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

2:34:15

okay and then

2:34:21

so we know that that one's working so we can get rid of that and then within our assets controller

2:34:27

on store see it could be the way that i'm getting it i might be misremembering

2:34:32

that portion right there

2:34:50

tell you what

2:34:57

let's debug it and see don't think i have my debug key in here

2:35:03

yet their script

2:35:15

okay and then i am not going to use fire well i can't use firefox for this

2:35:22

but i do not like firefox with debugger um so let's see i what i have edge

2:35:30

and then i need to restart my server using excuse me got something stuck in my throat

2:35:44

okay so i gotta restart my oop i'm gonna restart

2:35:50

my server npm run debug

2:35:56

go back to edge go back to localhost 33833

2:36:02

you need to sign in oh

2:36:08

bollocks

2:36:36

okay there we go

2:36:43

um so we'll go ahead get into that form

2:36:50

right and then let's inspect and then we'll open up the node debugger

2:36:58

verify that i actually put that debugger keyword in there yep cool so what i want to do is i want to

2:37:05

scope out one is post id actually something

2:37:10

two is it on this only

2:37:15

call or do i need to access it somewhere else i don't know if multi-part changes things i don't know

2:37:22

if having a manually processed route which this is

2:37:28

changes things that's why i'm not digging into the documentation for this per se but rather just going straight to

2:37:34

a debugger so let's go ahead try to upload an image here so

2:37:43

it's coming here

2:37:50

scroll down scroll down some more go to you and let's try you

2:37:55

all right let's open you up all right we're at our debugger point let me hit next so that it actually goes

2:38:01

to the proper place so post id is indeed undefined here

2:38:07

our request let's dig into you

2:38:15

so the body is empty and the data is empty parens are empty but that makes sense we

2:38:21

have multi-part data it could be in here

2:38:27

inside of fields fields looks empty

2:38:33

i really don't think it would be in files that would be the actual file

2:38:42

okay let's try

2:39:06

hmm i was just i'm already in request so i'm not gonna take the request again

2:39:12

um all right it doesn't really look like it

2:39:19

went up with it so

2:39:24

i must not have been seeing the uh formed data wrong it must not be sending

2:39:30

up but i don't know why it wouldn't be sending up it's in there it has a value

2:39:39

so i'm gonna go ahead and see i don't know if i can

2:39:46

i don't think chrome lets you inspect anything network wise until it resolves

2:39:55

but we'll try yeah yeah yeah no there it is that's

2:40:03

it's there

2:40:09

okay i wonder if i need to get it the exact same like request multi-part

2:40:15

form data or whatever that was that i was seeing in there

2:40:23

it's like i was saying this is a manually processed route um so

2:40:29

it's perfectly possible that adonis doesn't have any of the information with any of the data that was sent

2:40:36

in terms of like having it populated directly onto the request so

2:40:41

let me scope in on the multi part maybe it's in there

2:40:48

if it is in there i would imagine it would be on the fields but i'm not seeing it there

2:40:56

but then again i don't see the file on the files either but that's definitely there

2:41:01

so maybe it i don't know

2:41:08

well that does take a callback too though doesn't it

2:41:19

i could just make this easier on myself and pass it up as a param

2:41:25

uh but i would love to have the not need to define a separate route for

2:41:31

it

2:41:47

yeah i know i'm gonna take a look at the documentation real quick because i do have the manually processed

2:41:54

kind of mentioned a little bit in here um under the direct file uploads

2:42:06

so like that's that's managing the stream

2:42:24

oh gotcha not going to be available until after

2:42:29

the process that makes sense so i should have started with the documentation

2:42:36

okay let's continue on with that we'll accept the failure at that point we'll close out of this um

2:42:42

and close out of that close out and i'll undo that so

2:42:48

not available until after the process so we need to remove this what are they what what what are

2:42:53

they using input inputs query string in it

2:42:59

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

2:43:06

able to get it off of the request is what i'm getting at so we'll try that see if that works

2:43:12

um so we'll save that and we'll give that another go

2:43:18

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

2:43:25

server back into watch mode so that actually picks up the changes

2:43:31

let me run dev there we go and i'm going to close edge there we go

2:43:37

all right i just saw you refresh but i'm going to do a manual refresh just for sanity sake

2:43:45

and all right let's try this again um this image would have been up there a

2:43:51

couple times by now so let's do nope definitely not a psd all right

2:43:56

let's do this one all right so let's open it up

2:44:02

all right we got back some errors and it was the yeah

2:44:08

is it still the same issue yeah no no

2:44:14

no this time it's on sword order cool so that means that we're getting the post id yeah

2:44:20

so that's that's working so now yeah um instead of updating it like that

2:44:27

we want to get the most recent sort for the post as well so

2:44:35

const post

2:44:40

asset max sort not the best name but let's roll with it awaits post

2:44:47

dot query dot where id

2:44:53

is post id and

2:45:02

no sorry not post undo's undo undo undo

2:45:07

um where uh so we want the intermediary table so we'll want to do database

2:45:15

from um what asset posts is what it's called

2:45:20

asset posts

2:45:26

okay and then all we need to do is order by the search

2:45:33

order oh well we also need to do the where right where um

2:45:40

post id is post id and then order by descending

2:45:46

of the sort order and then

2:45:51

first no we don't want to fail but we do

2:45:58

want a default

2:46:06

you know be a nice extension to this as first or default

2:46:15

um okay so wait zero default it to zero um

2:46:24

okay i think i have that right so we'll get the

2:46:30

max sort for the asset post table for the post id descending order of the sort order if

2:46:36

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

2:46:41

zero so then we'll change this from an array

2:46:47

to an object with the

2:46:52

what is this the post id post id so that would be post id and

2:47:00

sort order of post asset max sort

2:47:08

plus one or we could just do plus plus okay it doesn't like that so we'll do

2:47:14

plus one and now that should save the sort order

2:47:20

along with the post so that should get rid of that error and

2:47:25

i might move that into a service because that kind of prolongs this already kind of

2:47:31

long function a little bit more um so this failed so we'll go ahead

2:47:38

uh i doubt that delete's gonna work but we'll try deleting it yeah i didn't think it would

2:47:45

what'd you fail on out of curiosity yeah i never saved into the database but it's saved up on the server okay

2:47:51

gotcha so we'll just refresh um we'll try this one more time

2:47:57

so all right let's move into one of the quick nope that was that was a quick tip

2:48:02

uh let's do one of the old thumbnails all right

2:48:08

all right yeah cool so that seems to have worked we got back at 200 um with the asset response

2:48:16

and it updated here okie dokie and if we refresh here we're

2:48:22

going to see us several images but we should see one in particular that is yeah the adonis 5 controller's

2:48:30

image that we just uploaded and now this file should be bound to the post without

2:48:37

saving so if we refresh it should still be there fingers crossed

2:48:47

all right cool cool and then if we delete it it should go away

2:48:53

and looks like it did should be gone from here should be gone from the database as well

2:49:04

actually i never even but yeah we can tell by the file name and it's not in there anymore um

2:49:10

and then if we refresh here it should have no issues and not show so

2:49:16

cool and now let's go ahead and before we wrap up let's go ahead and test multi files so

2:49:22

upload u um i think i have the yeah the uploaders just one at a time so we'll have to do

2:49:28

this one at a time but there's one um do you there's two

2:49:34

oh no ah

2:49:40

insert into asset posts invalid input syntax for type integer

2:49:47

object object oh yeah okay

2:49:53

so this isn't necessarily returning back just the number it's returning back the whole record

2:49:59

uh results [Music] but i think since this is a database module if we

2:50:05

just select a single column maybe it would return back just the number

2:50:11

let's see so we just want to get back to sort order

2:50:22

what post id is this three yeah

2:50:28

what is it what would it what i think it's a load db yeah load

2:50:34

db so we'll do a wait where's my code

2:50:42

await database from asset posts

2:50:47

there it is it's just db in here

2:50:54

dot where post id is three

2:51:01

order by sort order descending

2:51:11

dot select sort order

2:51:17

dot first right yep

2:51:23

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

2:51:29

out of that object so we'll do

2:51:35

sort order as post asset max sort

2:51:41

there we go so now this should work

2:51:47

oh wait a minute the other the or needs to now be

2:51:53

yeah or be an object of sort order with negative one there we go all right so that's not very clean

2:52:01

but it should work i'll always clean it up later i always say that but boy does that

2:52:07

really ever happen um so if we reload we should still have this username sign in and post crud

2:52:14

thumbnail which we do and now if we select our second image

2:52:22

i will do this one awesome so that upload

2:52:28

uploaded as well we'll do a third one

2:52:34

and that one went as well so now if we check the database we should have three

2:52:39

images tied to the post oh this is just the asset table but we

2:52:45

have all three of those images there and if we check this table we should have three images

2:52:52

tied to the post with an id of three which we do and

2:52:57

without saving if we go ahead reload all three of those images should still be there which they are

2:53:04

and then we can save go back to edit and it should still

2:53:12

be there can't access property after k is

2:53:18

undefined um let me look up one more can't access property after k oh that

2:53:24

says the same thing okay

2:53:29

where are you getting k from it's definitely in the image loop

2:53:38

all right it's the one feedback i have from alpine

2:53:44

as their errors could use some work but apart from that it's a great package um

2:53:51

expression images so if i had to guess

2:53:58

images is undefined and that's what this k is

2:54:04

so why would images be undefined well we've tried refreshing

2:54:12

and everything worked but whenever we actually saved the asset ids

2:54:18

must not have sent up and we are calling sync so if they don't

2:54:23

send up then they get detached so my guess is

2:54:30

maybe they got detached oh no that's even worse they got

2:54:35

reattached okay am i calling attach instead of sync

2:54:43

on sync assets that would be a brain forward if i am let's see where is that that's in the

2:54:48

post service

2:54:54

yep call and attach that should be sync so the good news is

2:55:02

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

2:55:11

let's see how that handles that so each of these have their own unique id the sort order

2:55:18

duplicated the asset id is duplicated i have to assume nothing's going to handle this so if we refresh we're probably

2:55:24

going to get six assets no we're still going to get an error

2:55:30

well that makes sense because we really didn't fix anything we just changed how it was stored

2:55:39

cannot access property after k is undefined so are we getting back our images at all are they coming back

2:55:46

let's go into our creator edit

2:55:51

and inspect post dot assets

2:56:00

yeah they're they're there okay so it's

2:56:07

probably a duplicate key issue if i had to guess maybe within that loop

2:56:15

um where is that key

2:56:20

right here oh no that's the image id

2:56:27

yeah no that still would be duplicate wouldn't it huh no yeah

2:56:32

yeah it would it would be it's that's not the that's not the intermediary table id that's

2:56:40

so i know well i don't know how to do index

2:56:46

in alpine i don't think it's provided as a secondary argument to the x4 i think it's

2:56:52

more like angular where it's just index uh

2:56:58

i don't know i'll try that and oh no it's not that okay

2:57:05

try the other person maybe it is more like view now i thought it was like angular

2:57:10

yep so it's an index issue um essentially duplicate ids

2:57:19

well that should never happen but now that we're calling sync anyway um

2:57:29

but without before we save that we can fix that by deleting oh no that deleted both of them

2:57:36

oh well um all right we'll we'll keep two we'll

2:57:42

just go go into the database and clean that up so i would imagine that only oh no that did okay

2:57:48

so let's see you and you you go away okay

2:57:55

because we still want some on here to test with and then now if we change this back to image id

2:58:01

should be okay and now if we go back to post them back in it should still be okay

2:58:07

yeah and now if we republish it should not have reduplicated itself

2:58:14

because we're calling sync but they should still be there yeah cool

2:58:20

so now if we come back and we should see the exact same thing as before awesome

2:58:25

last thing to check with this it does create still work or did i

2:58:32

completely break it so so far so good uh let's give it a title

2:58:41

and let's give it an image

2:58:46

yeah all right i bet you it's something to do with that

2:58:52

yep all right

2:58:58

so this is going to be within the assets controller

2:59:06

we need i thought i'd put that in a if but evidently i did not so

2:59:11

let's do if post id and that should do it

2:59:19

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

2:59:26

so the alternative approach to that is to immediately create a post whenever you

2:59:32

hit new post and then have anything that you do within here just update so then this page will just

2:59:38

always be an update page and not a create page that's the alternative approach that i could do but for right

2:59:44

now i don't see this as being that big of an issue um i never really write in here anyway i

2:59:49

always just import so that won't be too big of an issue um so

2:59:55

let's go and try okay cool so we're back to working now

3:00:01

try another image just for sanity's sake make sure i'm not doing a psd

3:00:08

there we go okay and let's give it a title

3:00:16

and let's publish so actually

3:00:21

yeah okay so i don't need to publish it okay cool

3:00:28

so creation's working editing is working those could use a loading icon but

3:00:35

also they're not they're still playing showing the full size so that's the last thing that we could do

3:00:41

is tack on a file size to that uh actually so let's go

3:00:48

but where is that that's in the file manager

3:01:00

yeah so that do it inside of the template actually

3:01:06

um so on the end of the image

3:01:12

where's the source here's the source we will change that into

3:01:18

ternary they're not a ternary but uh this is just a string

3:01:23

that whatever that backticks are called it will tack on the query string of

3:01:30

width don't think that's quite one

3:01:36

what is your width 176. so we can just let's round up to

3:01:42

200. so we give it a save and now those images should come through

3:01:47

as actually 200 pixels wide

3:01:52

and voila 200 pixels so that cuts back on the amount that it actually needs to load and

3:01:59

they're a little bit pixelated um although i am on a high resolution

3:02:05

display so it should be more like 400 so all right let's change that to 400

3:02:12

i could just put a source uh image picture source with like the

3:02:18

multi whatever but not that big of a deal especially not in

3:02:23

the back end where seo is not an issue at all so there we go that's much more clear

3:02:28

and there should now be 400 pixels cool and so now if we go into

3:02:37

i don't know whether or not based off of looking at the code it should be on the root cache

3:02:42

nope it sure is not so it must be in local i must have that

3:02:48

scope in it somehow so cash there's my seven so now we should see

3:02:53

oh what what are those images um installing lucid and getting data by the

3:03:00

users i don't know what i called that one but if we go into i think it's this one

3:03:07

so if we go into this one now nope either that or it's not saving

3:03:13

right let's just say 11 minutes ago so undefined

3:03:18

that's not ideal one hour

3:03:24

ago all right well all right let's find so the other one was installing those those are ones

3:03:30

where i actually named it after the lesson so it's going to be one of these two uh

3:03:37

i'm gonna take a gander and say that that timestamp's bigger yeah so here we go

3:03:45

so i need to fix that where if it's not defined it doesn't come through but so

3:03:52

you can see this width undefined quality on the fine which should just not be there

3:03:58

is for the normal full size image and then we have width 400 which is the 400

3:04:03

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

3:04:09

working just to give you a visual of what i was talking about earlier apart from that i think that this is a

3:04:15

good stopping point for today we what we went on three hours today so a half hour longer than we have been

3:04:21

doing um but we got the images fully working for our post the other side of

3:04:26

the equation is to actually show them which we have already working we just need to actually implement it on the

3:04:32

front end side so yeah lots of fun stuff ahead lots of fun

3:04:38

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

3:04:43

images all of this is up on the github which is down in the description linked down in the description

3:04:50

um so you can always check all this out as well i'll be pushing this up just after i end the

3:04:56

stream so thank you all for stopping by i appreciate your time hopefully you learned something hopefully this was

3:05:02

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

3:05:08

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.

robot comment bubble

Be the first to Comment!