Okay, boys, welcome back to the DriveSinker project. In the previous couple of videos, we finished the new gitignore parser that we were going to write. So, a custom parser for the gitignore file that could understand all sorts of complex patterns. We built that and integrated that with our sync algorithm, and with that, our project is mostly complete.
Like, you could just take this and put it into production and use it as a real project, and it would work fantastic, right? But there's a couple of quality of life features that I want to add right now, and one of them, let me just show you. I'm just going to delete this token file over here that I have, right? So I have to re-authenticate my account again, right?
And then let's run the entire application over here, and you'll notice I have to obviously re-authenticate my account. But let me just do this and show you. And I guess before we actually do that, I should stop redirecting the logs to the other file, because then I won't be able to see, right? Let's run this again.
So, as you can see, it gives me a URL to go to to authorize my account, right? Let's do that. And I just finished authorizing my account, and you can see it redirected me to localhost like it was supposed to. And here's the code right in the query parameters over here.
Now I'm supposed to copy this entire code and paste it into the terminal myself, right? And that's a bit, you know, cumbersome to do every now and then, right? Every time I need to re-authenticate my account, I have to go and copy paste this code from this broken page over here. And it just doesn't look good, right?
So, this is a quick little flow chart that I just drew to explain my thought process. But basically, what we're going to build is a very small, very minimal, temporary kind of a web server that just runs at the start of our program. It runs on localhost port 8998, and it will automatically fetch the code from the URL, right?
And once we've re-authenticated our account, this web server will just shut down, right? Because there's no need to keep it running after that, right? And if the token hasn't expired, then there's just no need to, you know, do anything, right? We just run the CLI as normal, right? So that's what we're going to build in this video. Let's get started.
Okay, to get started, first of all, go over to the client.json file where you have the OAuth client details, right? I'm going to make sure to blur out all of these client secrets and all the sensitive data so you guys can't hack me. But down here, in that redirect URIs key over here, we want to add the URL localhost 8998 slash google oauth redirect. And of course, you guys can use whatever URL you want. This is just what I'm using over here.
Save that, and let's try running the entire program again. And this time, if I run this, you can see this time it redirected me to my OAuth redirect URL over here, right? On port 8998. We still have the state, we still have the code, we still have all of those other things, but we also got to be able to redirect ourselves to this URL over here, which we can use to run our web server and fetch the code, right?
So I'm gonna close this down now because it's working, and now I'm going to create a new package over here in my project. Call it web, and inside I'm going to create the file web_server.go.
Now let's create a couple of functions over here. I'm going to create the function `startHTTP` to start our web server. It'll take a port that'll be an integer. It'll take a context, which will just be `context.Context`, just so we're able to shut down this web server once we've actually got the token, right? Because we don't want to keep it running after that.
And down here, I'm also going to create a redirect handler. I'll call this `OAuthRedirectHandler`, I guess. It'll take a response writer, I guess, and then a pointer to an HTTP request as well.
To get the code, we can just use `r.URL.Query().Get("code")`. There we go. And then I'm just going to say `fmt.Fprintf`, pass in the response writer, and then say something like, "Your auth code is," and then print the auth code over here, and then I'll add a new line, and I'll say, "Please paste this into your terminal," and that's it.
And then I can just pass in the code over here and make sure to add a colon over here so you actually create the variable. I forgot to do that, as always.
Now, back in the `startHTTP` function, very first thing I'm going to do is going to create a new mux by using, saying, `http.NewServeMux`. Add a handler over here, not `Handle`, `HandleFunc`. There we go. The pattern will be `/google/oauth/redirect` or whatever you set as the redirect URL in your client.json file, right? And then the handler will just be this function we just wrote over here.
Then let's create a server variable. This will be `http.Server`. The address will be `fmt.Sprintf`, pass in localhost and the port number that we get in the argument over here. And the handler for this will be, what the fuck was that? Handler will be the new mux that we create over here.
And this should be a pointer to `http.Server`, not the actual object.
Now you might think, why am I not using `http.ListenAndServe` or something as like a minimal way to run this web server? It's because `ListenAndServe` is not going to allow us to be able to shut down the server programmatically, right? When we're done with it.
You'll see what I mean once we actually write the full code. So just follow along. I'm going to create a new goroutine over here. It's just going to listen for this context object to just be complete, right? So imagine this is a cancelable context, right? Once the caller has cancelled this context, that's when this line will finish executing and will shut down the server over here.
And let's also add a log for that. Let's say, "Web server shutting down." Create a shutdown context over here and a cancel attribute as well. Let's say context.WithTimeout, because we want to have some sort of a timeout over here. That's just good practice, right? Pass in context.Background as the parent and a one-second timeout as the timeout. And then let's run defer cancel over here so that if we exceed the timeout, then we just forcefully shut down the web server, right?
And then let's say if error equals server.Shutdown, pass in the shutdown context as well. And let's say error does not equal to nil, then we'll say log.Printf("Error shutting down web server"). Print the error over here with a new line. Pass in the error. There we go. And then just run this goroutine at the bottom over here. And again, I forgot to add a colon over here. My bad.
So let me explain what's going on over here. Once the parent has cancelled this web server, right, this done function will finish executing and we will shut down the web server. We add a one-second timeout over here so that if there's any request coming in, then it has some level of some amount of time to finish executing and then we can shut down the web server. But if we do exceed that one-second timeout, then we'll just forcefully shut down the web server, right?
We don't need this because this is a local web server and its only purpose is to fetch the authorization code, right? So we don't need this at all, but it's a good practice to have all the same.
Now let's actually run our web server, and I'll say, "Web server listening on port," and then pass in the port number over here with a new line as well. And then we'll say if error equals server.ListenAndServe, error does not equal to nil, and error does not equal http.ErrServerClosed, right? We want to make sure we're not handling that server closed error because we literally do shut down the server over here manually. That's not something that is undesired in our program, right? So we don't need to handle that error.
And if there is an error over there, then we'll just say, "Error starting web server," %v to print the error and a new line at the end. And that's all we need to do to build this web server. Like I said in the beginning, this is super minimal.
Finally, we just go over to main.go to actually connect this web server and run it, right? Up top over here, I'm going to say web context and web cancel is equal to context.WithCancel(context.Background()). Then I run a goroutine over here, and this will run the web server.
Import the web package that we just built over here and run the StartHTTP function. Pass in the port. I guess it's 8998 for our purposes, and pass in the web context over here and run the goroutine.
That's all we need to do. So this is going to run the web server, and then in the actual main thread over here, we're going to run the ConnectToDrive function. This will give us the authorization URL. We'll go over there, we'll authorize our account, and get the code, which will redirect us to this web server. We'll paste in the code, and once we're done with that, that's when we'll run the web cancel function to shut down the web server because at that point we have connected to the drive and we're done, right?
As soon as this, as soon as this ConnectToDrive function is finished executing, we know we've connected to the drive and we can safely shut down the web server.
So that's literally all we needed to do. Let's run this just to make sure that I didn't make any mistakes. And there we go. You can see it says, "Web server listing on port 8998." So let's go and authorize my account.
And I should probably zoom in over here, but you guys can see we got redirected to our web server. It's running, and we can just grab this code over here and paste it into the terminal.
Now you might think, how is this any different from what we had before? We still had to copy the URL, copy the code from over here in the URL, right? Now we just need to copy it from our page over here, right?
Well, the difference is that you can actually, and let me just paste this over here, and you can see it. It got the token, and now it's going to start the sync process, but we don't care about that.
The real beauty of this approach is that you can have anything over here in the redirect handler over here, right? You can build a fancy page over here with a copy button and everything, and you can make it a lot easier for your users to get the auth token.
For my purposes, I'm just using a very basic implementation over here, literally just plain text over here, right? Just to show you that this is possible. You guys can create a HTML template that looks really fancy with all of the, you know, special effects and CSS and everything, and this can look really nice.
Anyway, that's all for this video. Like, comment, subscribe, share, and whatever else other YouTubers ask you to do.
And real quick, before you click off, this is my personal website, and this is where I have all of my work and portfolio and everything. I work as a freelance developer, building websites and web apps and mobile apps for people, and I'm always looking for work.
So if you're interested in hiring me, or if you know someone who's interested in building some sort of a web app or mobile app or something, click the top link in the description and book a call with me, and we'll discuss your project and we'll get to work.
That being said, guys, once again, thank you for watching the video, and I'm going to see you in the next video, which is going to be about—I have no idea what.