Displaying Database table data on Html table tag part 2
Hey! welcome again. Hope the first post didn’t make you finish all the coffees in Walmart?
This is part 2, it involves using cookies and sessions with a small not too middleware-kind of user-auth.
One minute… Taking the last coffee in walmart. Okay where were we…
Now the part two will basically have what we got in part one.
Added features:
- cookies.
- sessions.
- user authentication.
What are cookies? “a small sweet cake, typically round, flat, and crisp.”. Oh no! not that ‘cookie’.
- - a small amount of data that we can associate with a user
- - every time the user comes back to our site
- - that cookie is persistent (if the user didn’t delete it).
- - it stays between repeat visits to our site
- - “a database stored on the user’s computer that’s passed with every request”
- - cookies are a tool for us as web developers
- - we can use cookies to know if we have seen the same user before
- - makes it possible for us to implement logins/sessions
- - cookies are the most common ways to implement sessions
- - in Europe, you have to notify your user that you’re using cookies
- - you can see cookies in chrome browser
- - under dev tools under the application tab
every time a user goes to a domain, if there’s a cookie from that domain on the user’s computer, that cookie is sent along with the user’s request to that server. The server can use this info to know the user’s identity.
downside of cookies
- user can change the content
You can read more.
Sessions
I think we got court sessions right?
- - a user logs in
- - until they logout, that’s their session
- - sessions create “state”
- - we want to know a user is logged in for each of their requests
- -if you refresh
- - the cookies value remain the same
- - if you delete the cookie
- - you get a new UUID(Universal Unique IDentity)
- - to log a user out
- - delete the cookie
The App
I want only the admin to carry out the CRUD operation while the user just edit a description of self.
So am not gon’ go through all the creating of tables and different route and functions.
But let me just edit the database table to include description column. So this is how it should look like:
this is my directory:
by the way I’m using the awesome Ubuntu 16.04 LTS
We’re going to update the index page. But for the create.html, read.html, update.html and delete.html are the same. Just change the directory.
the main.go file:
this function runs when the server starts.
func init() {
templ, err = templ.ParseGlob("templates/*.html")
if err != nil {
log.Fatalln(err.Error())
}
adtempl, err = adtempl.ParseGlob("templates/admin/*.html")
if err != nil {
log.Fatalln(err.Error())
}
}
then the main function:
func main() {
handleDbConnection()
http.HandleFunc("/", index)
http.HandleFunc("/login", login)
http.HandleFunc("/logout", logout)
http.HandleFunc("/create", createUser)
http.HandleFunc("/read", readUser)
http.HandleFunc("/update", updateUser)
http.HandleFunc("/delete", deleteUser)
http.Handle("/assets/", http.FileServer(http.Dir("."))) //serve other files in assets dir
http.Handle("/favicon.ico", http.NotFoundHandler())
fmt.Println("server running on port :8080")
http.ListenAndServe(":8080", nil)
}
this is my landing page function. Now I came up with a concept of having a multi-purpose index page. So one page serves everybody and still serve a logged in user. Only the admin has a different index page. Okay enough of talk lets see it; the new index page:
func index(res http.ResponseWriter, req *http.Request) {
cookie, err := req.Cookie("logged-in")
title := "Home || cb_net sessions"
var isAuth int
if err == http.ErrNoCookie{
cookie = &http.Cookie{
Name: "logged-in",
Value: "0",
}
} //check for the value of the cookie to know which page to execute. If it's 1, it therefore means it's admin
//call the admin template. If it's 2 it therefore means it's a user, so set the isAuth variable to 2 cos we
//are going to check this in the html page else set it to 0 if cookie.Value == strconv.Itoa(1) {
isAuth = 1
var pd = PageData{Title: title, Username: uName}
err = adtempl.ExecuteTemplate(res, "index.html", pd)
if err != nil {
log.Fatalln(err.Error(),"no admin index")
}
fmt.Println("success")
} else if cookie.Value == strconv.Itoa(2) {
isAuth = 2
//get the description column on the db and display
describeUser(uName)
//if the request method is not post don't call the method. If you don't do this, every time the page
//loads, updateDescription is called.
if req.Method == "POST" {
describe := req.FormValue("description")
updateDescription(describe)
http.Redirect(res, req, "/", 302)
return
}
fmt.Println("success")
} else {
isAuth = 0 }
//at the end execute the template and pass the parameters.
var pd = PageData{Title: title,Username: uName, Description: des, IsAuth: isAuth}
err = templ.ExecuteTemplate(res, "index.html", pd)
if err != nil {
log.Fatalln(err.Error())
}}
Let’s now look at the login:
func login(res http.ResponseWriter, req *http.Request) {
if req.Method != "POST" {
err = templ.ExecuteTemplate(res, "login.html", nil)
if err != nil {
fmt.Sprint("error", err)
}
return
}
//get the username and password from the html>form>component, whose name is = username and
//password
var username = req.FormValue("username")
password := req.FormValue("password")
var pword string
var isAdmin int
// query the db and check if the username entered matches any in the db
rows, err := db.Query("SELECT password,is_admin FROM Crud_tb WHERE username = ?", username)
if err != nil {
log.Println(err.Error())
http.Error(res, "there was an error", http.StatusInternalServerError)
return
} //loop through the db to check requored details
for rows.Next() {
err = rows.Scan(&pword, &isAdmin)
if err != nil {
log.Println(err)
http.Error(res, "there was an error", http.StatusInternalServerError)
return
}
//if the row username matches, and the is_admin column = 1 compare the password.
if isAdmin == 1 {
// Validate the password
err = bcrypt.CompareHashAndPassword([]byte(pword), []byte(password))
// If wrong password redirect to the login
if err != nil {
fmt.Println("invalid")
http.Redirect(res, req, "/login", 301)
return
}
//if all goes well, assign username to the global variable uName so I can use it anywhere.
//Now create the cookie and set it so when redirected back to index, the index checks will verify
uName = username
cookie := &http.Cookie{
Name: "logged-in",
Value: "1",
}
http.SetCookie(res, cookie)
http.Redirect(res, req, "/", 302)
return
} else {
// Validate the password
err = bcrypt.CompareHashAndPassword([]byte(pword), []byte(password))
// If wrong password redirect to the login
if err != nil {
fmt.Println("invalid")
http.Redirect(res, req, "/login", 301)
return
}
uName = username
cookie := &http.Cookie{
Name: "logged-in",
Value: "2",
}
http.SetCookie(res, cookie)
http.Redirect(res, req, "/", 302)
return
}
}
}
We’ve done login, let’s do logout. Now this should be simple, since we can a cookie, we should be able to take it and burn it or break it or even eat it. Okay am talking, let go do the talking:
func logout(w http.ResponseWriter, r *http.Request) {
// MaxAge=0 means no 'Max-Age' attribute specified.
// MaxAge<0 means delete cookie now, equivalently 'Max-Age: 0'
// MaxAge>0 means Max-Age attribute present and given in seconds
http.SetCookie(w, &http.Cookie{
Name: "logged-in",
MaxAge: -1,
})
http.Redirect(w,r,"/", http.StatusSeeOther)
}
let’s see the describeUser function:
func describeUser(username string) {
// query
rows, err := db.Query("SELECT description FROM Crud_tb WHERE username=?", username)
if err != nil {
log.Println(err)
} var describe string for rows.Next() {
err = rows.Scan(&describe)
if err != nil {
log.Println(err)
}
des = describe
//return
}
}
and then be able to update the description of a user.
func updateDescription(describe string) {
//update user description
stmt, err := db.Prepare("UPDATE Crud_tb SET description=?, updated_at=? WHERE username=?")
if err != nil {
log.Println(err)
} rs, err := stmt.Exec(describe,time.Now(), uName)
if err != nil {
log.Println(err)
} affect, err := rs.RowsAffected()
if err != nil {
log.Println(err)
} fmt.Println("row :",affect," affected")
}
That’s for the mian.go file So now lets take a look at the index page:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{{ .Title }}</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" type="text/css" href="/assets/css/tachyons.css">
</head>
<body>
<main class="flex flex-column min-vh-100"><!-- use flex to separate the layouts into diff sections. -->
{{ if eq .IsAuth 2 }}
<header class="w-100">
<div class="w-100 w-50-m w-30-l dib bg-washed-blue">
<a href="/" class="f3 f1-ns fw6 hover-blue link pointer black-80 dib ph3">CRUD</a>
</div><div class="w-100 w-50-m w-70-l dib">
<span class="f4 f3-ns fw3 ttu black-80 ph3">sessions</span>
<a class="br2 shadow-2 blue f3">Hi! {{ .Username }}</a>
<a href="/logout" class="hover-blue link pointer black-80 dib pv3">logout</a>
</div>
</header>
<section class="flex-auto flex flex-column">
<div style="background: url(assets/img/bg1.jpg);" class="vh-75 cover">
</div>
<div class="pv4 pv2-ns ph2 bg-light-gray ph2 ph5-ns" style="background-image: linear-gradient(to right, rgba(30, 75, 115, 1), rgba(255, 255, 255, 0)">
<blockquote class="lh-copy">
<div class="tc">
<div class="dib">
<form method="post" action="/">
<div class="pa3"><label class="w-auto pv2 ph4 br3 shadow-2 white">{{ .Description }}</label></div>
<p class="white">edit description</p>
<div class="pv2"><input class="br2 w-auto" type="text" aria-multiline="true" name="description" placeholder="describe yourself"></div>
<div class="pv2 mv2"><input class="f5 fw6 dib ba b--black-20 bg-blue white ph4 pv3 br2 grow no-underline" type="submit" value="save"></div>
</form>
</div>
</div>
</blockquote> </div>
</section>
{{ else }}
<header class="w-100">
<div class="w-100 w-50-m w-30-l dib bg-washed-blue">
<a href="/" class="f3 f1-ns fw6 hover-blue link pointer black-80 dib ph3">CRUD</a>
</div><div class="w-100 w-50-m w-70-l dib">
<span class="f4 f3-ns fw3 ttu black-80 ph3">sessions</span>
<a href="/login" class="hover-blue link pointer black-80 dib pv3">login</a>
</div>
</header>
<section class="flex-auto flex flex-column">
<div style="background: url(assets/img/bg1.jpg);" class="vh-75 cover">
</div>
<div class="pv4 pv2-ns ph2 bg-light-gray ph2 ph5-ns" style="background-image: linear-gradient(to right, rgba(30, 75, 115, 1), rgba(255, 255, 255, 0)">
<blockquote class="lh-copy Helvetica f5 f3-ns">
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
</blockquote> </div>
</section>
{{ end }}
<footer class="bg-washed-blue pa4 tc">
<span class="f6">© connelblaze</span>
</footer>
</main>
</body>
</html>
Notice this line: {{ if eq .IsAuth 2 }}
yeah. at first i did this: {{ if .IsAuth == 2 }}
wasn’t working, even the docs wasn’t helping. I let go until #slack came to the rescue. Someone in slack told me how to do it.
So now what’s that? Is a conditional statement that checks if the value of IsAuth is equal to 2.
Remember I said 1 template to serve two different purposes? yeah this is it.
If it’s equal to 2 change the template else serve the default template.
This is the login:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>CRUD || Login</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" type="text/css" href="/assets/css/tachyons.css">
</head>
<body>
<header class="w-100 bg-washed-blue pv2 ph2 ph5-ns tc">
<div class="w-50 dib tc">
<h1>Login Page</h1>
</div>
</header>
<section class="w-100 bg-light-gray tc vh-75" style="background-image: linear-gradient(to right, rgba(30, 75, 115, 1), rgba(255, 255, 255, 0)">
<div class="w-60 dib">
<form method="post" action="/login">
<div class="pv2 mv2"><input class="b--white=10 br2 w-auto" type="text" name="username" placeholder="username"></div>
<div class="pv2 mv2"><input class="b--white=10 br2 w-auto" type="password" name="password" placeholder="password"></div>
<div class="pv2 mv2"><input class="f5 fw6 dib ba b--black-20 bg-blue white ph4 pv2 br2 grow" type="submit" value="Login"></div>
</form>
</div>
</section>
</body>
</html>
and this is the admin index:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>CRUD</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" type="text/css" href="/assets/css/tachyons.css">
</head>
<body>
<main class="flex flex-column min-vh-100"><!-- use flex to separate the layouts into diff sections. -->
<header class="w-100">
<div class="w-100 w-50-m w-30-l dib bg-washed-blue">
<a href="/" class="f3 f1-ns fw6 hover-blue link pointer black-80 dib ph3">CRUD</a>
</div><div class="w-100 w-50-m w-70-l dib">
<span class="f4 f3-ns fw3 ttu black-80 ph3">crud operations</span>
<div class="tr">
<a class="br2 shadow-2 blue f3">Hi! {{ .Username }}</a>
<a href="/logout">logout</a>
</div>
</div>
</div>
</header>
<section class="flex-auto flex flex-column flex-row-ns">
<div class="pv4 pv2-ns ph2 bg-light-gray">
<a href="/create" class="pv2 ph4 f6 fw3 fw6-ns hover-blue link pointer black-80 db">Create user</a>
<a href="/read" class="pv2 ph4 f6 fw3 fw6-ns hover-blue link pointer black-80 db">Read user</a>
<a href="/update" class="pv2 ph4 f6 fw3 fw6-ns hover-blue link pointer black-80 db">Update user</a>
<a href="/delete" class="pv2 ph4 f6 fw3 fw6-ns hover-blue link pointer black-80 db">Delete user</a>
</div>
<DIV class="pv4 pv2-ns ph2 bg-light-gray" style="background-image: linear-gradient(to right, rgba(30, 75, 115, 1), rgba(255, 255, 255, 0)">
<blockquote class="lh-copy">
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
</blockquote> </div>
</section>
<footer class="bg-washed-blue pa4 tc">
<span class="f6">© connelblaze</span>
</footer>
</main>
</body>
</html>
So this is it. You’ve done it
Now I did my in IDEA
After creating this, if you like to push to github
- create a repo on github
- click on vcs on IDEA
- move to git then select github.
- chose authentication mode; by token or password
- select newly created repo
- clone to go/src/github.com/username/<project>
- then copy files into the new project dir
- right-click and choose git
- everything should be clear from there
this is a link to my github repo:
Remember to gitignore your .idea as it contains some valuable infos.
Next one will be using gorilla/sessions
😂😂😂😂😂
don’t be scare of programming or learning new things.
Or just because you don’t get it as quick as others do.
Take your time, but don’t stop working.
You can buy me a coffee.
Stay turned!