تماشای این درس نیاز به اشتراک حرفه‌ای دارد.

Offline Sync Auth Problem0:00

We are finally ready to implement the offline sync, but there are some issues, but we're going to fix them. The issue is that we have data in our IndexedDB that we can't really sync because, well, yes, we have stored all of the information that we need. We are making a POST or a PUT request, and that requires something, some security like a CSRF token. Technically, we could use a CSRF token,

Installing Sanctum Setup0:31

some security like a CSRF token. Technically, we could use a CSRF token, but there's no guarantee that it would work. Instead, something that would work would be like an API token. Because what do we have here? We have a client and we can create an API token that is focused for making offline sync requests. Sounds good to me. That's what we are going to do. The first thing that we need to do is install Sanctum. We will use Artisan to install API.

The first thing that we need to do is install Sanctum. We will use Artisan to install API. This is going to install Sanctum for us. Now, there are some things that we will need to do. First of all, it's going to ask us to run the migrations, which when it gets to that point, yes, of course, we do. But then there's other things that we would need to do, like for example, go to our user model, because we need to include the has API tokens trait, so that we can actually do that.

because we need to include the has API tokens trait, so that we can actually do that. And well, it's still installing dependencies. The other thing that we need to do is go to the bootstrap folder where app.php is, because we need to set up a middleware alias for checking our abilities. Still installing dependencies, that's taken a while. Anyway, so our alias is going to be ability, because this gives us the ability to check for these abilities in our middleware.

Anyway, so our alias is going to be ability, because this gives us the ability to check for these abilities in our middleware. So as we are defining our routes, everything is just nice and clean and everything's going to work. So this is going to map to a class called, if we can get that right, check for any ability and class there. Now, we could also set up the abilities alias too, but we don't need that in this case. So I'm not going to worry about that. Right now, ability is going to be fine.

So I'm not going to worry about that. Right now, ability is going to be fine. Wow, that's taken a long time for that to install. So we'll just keep going. So with that done, really the first thing that we need is a controller that we can use to make requests, but we can't really do that because this is taking so long to install. Okay, so let's move on then, and we'll come back to that. Because what I want to do is essentially have code that is going to always ensure that we have a PWA token,

Creating PWA Token Storage2:52

Because what I want to do is essentially have code that is going to always ensure that we have a PWA token, and that's what I'm going to call it, a PWA token. So inside of resources.js.storage, let's create a new file called PWA token or PWA storage. Let's do storage. And we need to essentially fetch our token so that we can store it in local storage. Now, our service worker doesn't have access to local storage. So all this is for ensuring that when we need to save something offline,

Now, our service worker doesn't have access to local storage. So all this is for ensuring that when we need to save something offline, then we have the token available so that we can save it in index DB so that the service worker can get to it. So we're going to have the PWA, let's just call it token key, because we need a key in order to get and set data inside of local storage. We'll just call it PWA token. Then we'll have a couple of helper functions, one to get the PWA or let's call it get stored token. And all this is going to do is reach into local storage

one to get the PWA or let's call it get stored token. And all this is going to do is reach into local storage to get the item with our PWA token key. That's all that it's going to do. And we will have a set stored token function that is going to accept the token that we want to store. Once again, it's going to reach into local storage to set the item and PWA token key will have the value of token. All right. So, wow. Yes, we want to run our migration.

All right. So, wow. Yes, we want to run our migration. Great. Okay. So since we are here inside of our JavaScript, let's just finish this function. Then we will shift our focus there. So this is going to be an async function called ensure PWA token. Its goal is to ensure that we have a token. So the first thing we need to do is check to see if we have an existing token. So we will try to get the stored token.

So the first thing we need to do is check to see if we have an existing token. So we will try to get the stored token. And if we have a stored token, we're just going to return it because we've got it, we need to use it. But we should also check if we are offline, then we can't fetch it. So we're just going to return null because we need to return something. So we will just return null and that's going to be fine there. But if we make it past here, well, then we're good to go to make our request. So we need the CSRF token.

But if we make it past here, well, then we're good to go to make our request. So we need the CSRF token. And I wish I had just extracted this into a module to make this a whole lot easier to get, but oh well. So we want the meta tag with the name of CSRF token. And we want to get the content attribute so that we will have that. And then it's just a matter of making our request. So we will call fetch. Let's say that our end point to fetch our API token is going to be PWA token. It's real descriptive, isn't it?

Let's say that our end point to fetch our API token is going to be PWA token. It's real descriptive, isn't it? And this will be a post request. We need our headers because we need to include the X CSRF token header, which will have the value of our token. We should also have the accept header set to application JSON. And that's going to be our request. So let's make sure that the request was okay. If it's not okay, what do we do? Let's just throw a new error.

If it's not okay, what do we do? Let's just throw a new error. And we'll say that we failed to retrieve PWA token. Then we will return because there's nothing else for us to do. And I guess we should return. Well, we're throwing. We don't need to return anything. But if we make it past here, then we have a token. So we will get the data. We will await res JSON.

So we will get the data. We will await res JSON. That means we didn't await fetch. So we want to await fetch so that we can await res JSON, so that we can set the stored token to data.token. Because this is going to have what we are going to get from the server will be a token property. Then we will return data.token. Okay, so this is going to be on the client. And with that done, let's create a controller. We want to make a controller and we'll call it PWA token controller.

Building Token Endpoint7:08

And with that done, let's create a controller. We want to make a controller and we'll call it PWA token controller. And this can be invocable because we only want to do one thing with this controller. And that is get a token that we can use. So with that, let's open up our PWA token controller and let's get our user from the request. And the first thing I want to do is delete all of the existing tokens for this particular purpose. The idea being that it's entirely possible

is delete all of the existing tokens for this particular purpose. The idea being that it's entirely possible that a user is going to be simultaneously using our application on two different clients. The chances are slim though. So I want to ensure that we don't have any stale tokens for this purpose. That makes things a little more secure. And I think that's fine. So we want to delete all of the tokens for this user where the name is PWA token.

So we want to delete all of the tokens for this user where the name is PWA token. Then we will delete those. So that then we will turn around and we will create a new token. So we will call user, then we will call create token. The name is going to be PWA token, but then we also want to define the abilities for this token. And we'll call it sync offline. So that if a client has a token with this ability, they can then sync their offline data.

So that if a client has a token with this ability, they can then sync their offline data. We will get the plain text token so that then we can return our response, which will be a JSON structure. We will have a token property, which will just be our token value. And so there, we just need to set up our route for that. We of course want this to be only for authenticated users. So we'll do it inside of our group for the auth middleware.

We of course want this to be only for authenticated users. So we'll do it inside of our group for the auth middleware. We want to post request for PWA token. And that was for the PWA token controller class. Let's give this a name of PWA token. All right. So we have that all ready to go. We have the ensure PWA token function ready on the client. So now we just need to use this. Then I want to do this in a couple of places.

Using Token in App9:24

So now we just need to use this. Then I want to do this in a couple of places. First of all, inside of app.js. And I want to do this globally because the idea is that we provide this offline capability across our application. So it's not just for our meters, it's for everything. So we want our PWA token globally accessible so that when it comes time to save that data inside of index DB, we can include the token.

so that when it comes time to save that data inside of index DB, we can include the token. So we want to ensure that we have the PWA token and there we go. So I'm going to do this right here inside of app.js inside of the Alpine init. Ideally we would do it a little sooner but this is going to be fine. While we're here though, I want to disable our polling because when it comes time to actually test this

While we're here though, I want to disable our polling because when it comes time to actually test this and if we have to get into the network tab, I don't want to see a bunch of healths. It just muddies things up. So we are going to ensure PWA token here so that everything should work as far as getting that token globally. But then we need to go to our reading form because we need to ensure that we have a token here too

But then we need to go to our reading form because we need to ensure that we have a token here too before we save data. So we want to ensure the PWA token and where do we want to use this? Definitely inside of submit form. So if we are online, then we submit the form, we don't need it. But if we're not, then we do. So we prevent the default,

But if we're not, then we do. So we prevent the default, then we'll try to get our token here. We need to await this. And if we don't have a token, then let's write something to the console. We will warn saying that no token available and then we will just return because if we don't have a token, we can't sync it if it's saved.

because if we don't have a token, we can't sync it if it's saved. So there's no point in saving it at which point, now we can just say we're sorry and go on from there. It is what it is. So then we get our payload, we get our record and all we need to do is include our token as part of our record here. We save to the outbox, register sync,

as part of our record here. We save to the outbox, register sync, everything should be okay there. So as far as our service worker is concerned, we're not done because, well, we're not in the service worker yet, are we? No. So as far as saving the data is concerned, we are done because we have included the token in our record so we are good to go there.

Securing Offline Sync API11:50

we are done because we have included the token in our record so we are good to go there. So now we can go to our service worker and really we don't need the service worker, we need the process outbox function inside of the outbox processor because now as we read the data here, we now have the token. So we need to include the token with the authorization header,

So we need to include the token with the authorization header, it's going to be bearer space and then the item.token. So we include the token with the authorization header, but we also need to think about the endpoint. Now, because as it currently is, the endpoint is a web route. Let's open up our web routes. So we have two right now. The ones that we are concerned with is this post request

So we have two right now. The ones that we are concerned with is this post request and this put request. So the URLs are of course going to those things and we want them to go to an API endpoint. We could do something like this so that our API URLs just begin with API. The rest of the URL is going to be the same. So that means it will be easier to adjust our code here so that we don't have to make a lot of changes

So that means it will be easier to adjust our code here so that we don't have to make a lot of changes to get it to the right URL. We don't have to save anything extra or anything like that. While I'm looking at this, that's a different color because that's supposed to be response, not respose. So the coloring isn't updating, but that clearly is response. That saved us something. That would have probably taken me a while to find.

That saved us something. That would have probably taken me a while to find. Okay, so we need to essentially change the URL that we send this to. So I'm going to do this. We'll have this URL obj to where we will new up the URL constructor passing in item.endpoint. And then we will manipulate the path name of this URL obj object.

And then we will manipulate the path name of this URL obj object. All we are going to do is start it with slash API and then we will include the existing path name so that then all we need to do here is say URL obj href. And there we go. So that is going to change the URL so that it now points to our API. Everything else should be the same and we are good to go there.

Everything else should be the same and we are good to go there. And this is going to make things easy on us because yes, we will need another controller. So let's go ahead and create that controller. We'll call it offline reading controller. But I'm not going to say that it's the same thing as our reading controller. Let's open up the reading controller, but it practically is because we need the store method

Let's open up the reading controller, but it practically is because we need the store method for storing a new reading. We need the update in order to update a reading. So really we could lift these methods and put them inside of the offline reading controller. And I'm going to do that, but I've made the necessary changes. There's not much. I figured you probably didn't want to see me

There's not much. I figured you probably didn't want to see me type all of that out. So all I've done is I've taken the store and the update methods from the reading controller. I just made a few changes for the store method. Whenever we create the reading, we store it so that we can provide it in a JSON structure because this is an API endpoint. For the update, we update the reading

because this is an API endpoint. For the update, we update the reading and then we respond with a JSON structure that includes the reading because it's an API endpoint. That's it. It's the same exact thing. So ideally, we would have a service that would do all of this so that we have less duplication, but that comes later in the life cycle of the application. For right now, this is going to be fine.

but that comes later in the life cycle of the application. For right now, this is going to be fine. So now that we have that, let's open up our API routes. And what we want to do is essentially replicate the post and the put. So let's just copy those. And we want to ensure that these routes are protected by our auth sanctum middleware, as well as our ability to sync offline because we don't want just any API token

as well as our ability to sync offline because we don't want just any API token to make these requests. We want just those that have the ability to sync offline. So we will group those and then we will define our routes. We don't need the get route, so we'll get rid of that. But all we need to do here is change reading controller to offline reading controller. Okay, so as far as names are concerned, I'm just going to put API dot in front of these

Okay, so as far as names are concerned, I'm just going to put API dot in front of these because that's all that we really need to do. So now we should have everything in place. We have our routes set up. We should be able to fetch our API token correctly. So let's get into the browser. And we have an invalid arguments, attribute middleware does not exist. Of course it doesn't, that was API

attribute middleware does not exist. Of course it doesn't, that was API because you need to spell it correctly. So back in the browser. Okay, we need to unregister our service worker because we have made changes to it. And let's do a hard refresh. Now, by doing that, we should be able to go to our local storage and we should have our PWA token.

we should be able to go to our local storage and we should have our PWA token. We do, so we are good to go there. If we refresh the page, we have the same value for this token because we have retrieved it. We've stored it. We are good to go. Let's delete what we have for our field logger database because we now have a different record shape.

Let's delete what we have for our field logger database because we now have a different record shape. So let's delete that and let's cross our fingers. So let's go offline. Let's change this. So it's 1,865. Let's just round that down so that we have a whole number. We hit update. There aren't any errors.

We hit update. There aren't any errors. I could see that index DB now has some data. And if we take a look, sure enough, we have created that endpoint ID method payload and we have the token. So everything should be good there so that whenever we go back online, it should sync. Well, we don't have any errors here. So one way before we refresh the page,

Well, we don't have any errors here. So one way before we refresh the page, because now the value should be changed, we can go to our Outbox database. We can see that the data may be stale. Let's refresh and it's not there. So that's a good indication that we have just synced our data. So if we refresh the page, our value stays the same and there we go.

So if we refresh the page, our value stays the same and there we go. Providing our users the ability to save data offline and sync it when they are online is fantastic. There are, of course, a lot of moving parts, but it gives our applications, our web applications, a feature that is normally exclusive to native apps. I think it's worth the effort.

دوست دارید گاهی خبرهای Laracasts را ایمیل کنیم؟