From dfd0bb22a1bb0d2a568f9b47886e92026dc5a120 Mon Sep 17 00:00:00 2001 From: Nathaniel Mitts Date: Fri, 19 Oct 2018 12:17:28 -0400 Subject: [PATCH 01/62] DB scripts --- db/databaseSetAdmin.go | 28 +++++++++++++++++++++++ db/databseCreateV2.go | 32 ++++++++++++++++++++++++++ db/userTestV2.go | 52 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 112 insertions(+) create mode 100644 db/databaseSetAdmin.go create mode 100644 db/databseCreateV2.go create mode 100644 db/userTestV2.go diff --git a/db/databaseSetAdmin.go b/db/databaseSetAdmin.go new file mode 100644 index 0000000..486bbd2 --- /dev/null +++ b/db/databaseSetAdmin.go @@ -0,0 +1,28 @@ +package main + +import ( + "database/sql" + //"fmt" + _ "github.com/go-sql-driver/mysql" +) + +func main() { + set("homeworkHubUser") +} + +func set(name string) { + db, err := sql.Open("mysql", "root:password@/") + if err != nil { + panic(err) + } + defer db.Close() + + _, err = db.Exec("USE " + name) + if err != nil { + panic(err) + } + _, err = db.Exec("ALTER TABLE userInfo ALTER isAdmin SET DEFAULT false;") + if err != nil { + panic(err) + } +} diff --git a/db/databseCreateV2.go b/db/databseCreateV2.go new file mode 100644 index 0000000..595f9fa --- /dev/null +++ b/db/databseCreateV2.go @@ -0,0 +1,32 @@ +package main + +import ( + "database/sql" + //"fmt" + _ "github.com/go-sql-driver/mysql" +) + +func main() { + create("homeworkHubUser") +} + +func create(name string) { + db, err := sql.Open("mysql", "root:password@/") + if err != nil { + panic(err) + } + defer db.Close() + + _, err = db.Exec("CREATE DATABASE " + name) + if err != nil { + panic(err) + } + _, err = db.Exec("USE " + name) + if err != nil { + panic(err) + } + _, err = db.Exec("CREATE TABLE userInfo(user_id INTEGER NOT NULL AUTO_INCREMENT PRIMARY KEY, email VARCHAR(32) NOT NULL, username VARCHAR(32) NOT NULL, isAdmin BOOLEAN, passwordHash BINARY(60) NOT NULL);") + if err != nil { + panic(err) + } +} diff --git a/db/userTestV2.go b/db/userTestV2.go new file mode 100644 index 0000000..d25750a --- /dev/null +++ b/db/userTestV2.go @@ -0,0 +1,52 @@ +package main + +import ( + "bufio" + "database/sql" + "fmt" + _ "github.com/go-sql-driver/mysql" + "golang.org/x/crypto/bcrypt" + "log" + "os" + "strings" +) + +func main() { + userMake("homeworkHubUser") +} + +func userMake(name string) { + + reader := bufio.NewReader(os.Stdin) + // var username string + fmt.Println("Enter Username: ") + username, _ := reader.ReadString('\n') + username = strings.Replace(username, "\n", "", -1) + // var email string + fmt.Println("Enter Email: ") + email, _ := reader.ReadString('\n') + email = strings.Replace(email, "\n", "", -1) + + // var passwordhash string + fmt.Println("Enter Password: ") + password, _ := reader.ReadString('\n') + password = strings.Replace(password, "\n", "", -1) + hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) + if err != nil { + log.Fatal(err) + } + db, err := sql.Open("mysql", "root:password@/") + if err != nil { + panic(err) + } + defer db.Close() + + _, err = db.Exec("USE " + name) + if err != nil { + panic(err) + } + _, err = db.Exec(fmt.Sprintf("INSERT INTO userInfo (email,username,passwordHash) VALUES('%s', '%s', '%s');", email, username, hash)) + if err != nil { + panic(err) + } +} From 584fdecbddbaa323911f8b79221c2c5c29d5e6d2 Mon Sep 17 00:00:00 2001 From: Robby Zambito Date: Tue, 27 Nov 2018 14:55:21 -0500 Subject: [PATCH 02/62] Trimmed down the homework struct --- main.go | 6 ------ 1 file changed, 6 deletions(-) diff --git a/main.go b/main.go index 6778251..837bffb 100644 --- a/main.go +++ b/main.go @@ -25,8 +25,6 @@ func main() { Id: 123, Title: "[CS][370][Confer] First Homework", PostImage: "image1.jpeg", - Upvotes: 1, - Downvotes: 99, Comments: []string{"This post is great!", "No, it really isn't"}, Tags: []string{"2018", "MAT", "413", "Andriamanalimanana"}, }, @@ -50,8 +48,6 @@ func main() { Id: 123, Title: "[CS][370][Confer] First Homework", PostImage: "image1.jpeg", - Upvotes: 1, - Downvotes: 99, Comments: []string{"This post is great!", "No, it really isn't"}, Tags: []string{"2018", "MAT", "413", "Andriamanalimanana"}, } @@ -75,8 +71,6 @@ type homework struct { Id uint Title string PostImage string - Upvotes uint - Downvotes uint Comments []string Tags []string } From ccdb2e6b98be7f0f41c6dc84ed0c75471dcbebee Mon Sep 17 00:00:00 2001 From: Nathaniel Mitts Date: Tue, 27 Nov 2018 15:06:37 -0500 Subject: [PATCH 03/62] Drop Old Files, Add New Go DB Files for SQLite --- db/comment_section.go | 13 +++++++++++ db/databaseSetAdmin.go | 28 ---------------------- db/databseCreate.go | 32 ------------------------- db/databseCreateV2.go | 32 ------------------------- db/homeworkHub.db | Bin 0 -> 24576 bytes db/post_info.go | 13 +++++++++++ db/post_tags.go | 13 +++++++++++ db/userTest.go | 48 ------------------------------------- db/userTestV2.go | 52 ----------------------------------------- db/user_info.go | 13 +++++++++++ 10 files changed, 52 insertions(+), 192 deletions(-) create mode 100644 db/comment_section.go delete mode 100644 db/databaseSetAdmin.go delete mode 100644 db/databseCreate.go delete mode 100644 db/databseCreateV2.go create mode 100644 db/homeworkHub.db create mode 100644 db/post_info.go create mode 100644 db/post_tags.go delete mode 100644 db/userTest.go delete mode 100644 db/userTestV2.go create mode 100644 db/user_info.go diff --git a/db/comment_section.go b/db/comment_section.go new file mode 100644 index 0000000..7b5e5bd --- /dev/null +++ b/db/comment_section.go @@ -0,0 +1,13 @@ +package main + +import ( + "database/sql" + + _ "github.com/mattn/go-sqlite3" +) + +func main() { + database, _ := sql.Open("sqlite3", "./homeworkHub.db") + statement, _ := database.Prepare("CREATE TABLE IF NOT EXISTS comment_section (post_id INTEGER, username TEXT, comment TEXT)") + statement.Exec() +} diff --git a/db/databaseSetAdmin.go b/db/databaseSetAdmin.go deleted file mode 100644 index 486bbd2..0000000 --- a/db/databaseSetAdmin.go +++ /dev/null @@ -1,28 +0,0 @@ -package main - -import ( - "database/sql" - //"fmt" - _ "github.com/go-sql-driver/mysql" -) - -func main() { - set("homeworkHubUser") -} - -func set(name string) { - db, err := sql.Open("mysql", "root:password@/") - if err != nil { - panic(err) - } - defer db.Close() - - _, err = db.Exec("USE " + name) - if err != nil { - panic(err) - } - _, err = db.Exec("ALTER TABLE userInfo ALTER isAdmin SET DEFAULT false;") - if err != nil { - panic(err) - } -} diff --git a/db/databseCreate.go b/db/databseCreate.go deleted file mode 100644 index 6fe5424..0000000 --- a/db/databseCreate.go +++ /dev/null @@ -1,32 +0,0 @@ -package main - -import ( - "database/sql" - //"fmt" - _ "github.com/go-sql-driver/mysql" -) - -func main() { - create("homeworkHub") -} - -func create(name string) { - db, err := sql.Open("mysql", "root:password@/") - if err != nil { - panic(err) - } - defer db.Close() - - _, err = db.Exec("CREATE DATABASE " + name) - if err != nil { - panic(err) - } - _, err = db.Exec("USE " + name) - if err != nil { - panic(err) - } - _, err = db.Exec("CREATE TABLE userInfo(user_id integer NOT NULL AUTO_INCREMENT PRIMARY KEY, email varchar(32) NOT NULL, username varchar(32) NOT NULL, isAdmin varchar(6), passwordHash varchar(32) NOT NULL )") - if err != nil { - panic(err) - } -} diff --git a/db/databseCreateV2.go b/db/databseCreateV2.go deleted file mode 100644 index 595f9fa..0000000 --- a/db/databseCreateV2.go +++ /dev/null @@ -1,32 +0,0 @@ -package main - -import ( - "database/sql" - //"fmt" - _ "github.com/go-sql-driver/mysql" -) - -func main() { - create("homeworkHubUser") -} - -func create(name string) { - db, err := sql.Open("mysql", "root:password@/") - if err != nil { - panic(err) - } - defer db.Close() - - _, err = db.Exec("CREATE DATABASE " + name) - if err != nil { - panic(err) - } - _, err = db.Exec("USE " + name) - if err != nil { - panic(err) - } - _, err = db.Exec("CREATE TABLE userInfo(user_id INTEGER NOT NULL AUTO_INCREMENT PRIMARY KEY, email VARCHAR(32) NOT NULL, username VARCHAR(32) NOT NULL, isAdmin BOOLEAN, passwordHash BINARY(60) NOT NULL);") - if err != nil { - panic(err) - } -} diff --git a/db/homeworkHub.db b/db/homeworkHub.db new file mode 100644 index 0000000000000000000000000000000000000000..5eefb5d366c32afa58691e0151471b3aa5253e38 GIT binary patch literal 24576 zcmeI#O;5rw7{KvO-Y&jf3@1YqFBl0I51++1G-N{T@=8Tm>aTHBUwTgO6JmX+7FsB2nG-_*3&Y2%k? z-l|uhHuMLDjZbUqed}oRW3#TC6a)}J009ILKmY**5I_Kd{}XTu#q!RM{h<8&p;Z1u zY-;he?K++-Jm=)X6=pdlo_prDYi3w@?wz+kUC)=wm5M!07D+w~2hn$< zu&~_s@XDIvs@-Th?T$EiJHom3E*mX9v+1_Hns|xjB=kpe7E%Wf)G%!if}!k=ef5}Z z=QUR<9~{`zz1fj^(P$(?)s1CO1yQ)v%`fb1>E+7)R(3I;ki@-OE1I~SS=jh_s559C z_4!4HJ-O7)EreZqSvAM3>6!a)cB#C(Yfqiol18ARS<~m*!XoLYsQ%p1A3Vo?9KS`A zesUC(o9m>Xf&c;tAb Date: Tue, 4 Dec 2018 14:06:23 -0500 Subject: [PATCH 04/62] Delete homeworkHub.db Deleting .DB file --- db/homeworkHub.db | Bin 24576 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 db/homeworkHub.db diff --git a/db/homeworkHub.db b/db/homeworkHub.db deleted file mode 100644 index 5eefb5d366c32afa58691e0151471b3aa5253e38..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 24576 zcmeI#O;5rw7{KvO-Y&jf3@1YqFBl0I51++1G-N{T@=8Tm>aTHBUwTgO6JmX+7FsB2nG-_*3&Y2%k? z-l|uhHuMLDjZbUqed}oRW3#TC6a)}J009ILKmY**5I_Kd{}XTu#q!RM{h<8&p;Z1u zY-;he?K++-Jm=)X6=pdlo_prDYi3w@?wz+kUC)=wm5M!07D+w~2hn$< zu&~_s@XDIvs@-Th?T$EiJHom3E*mX9v+1_Hns|xjB=kpe7E%Wf)G%!if}!k=ef5}Z z=QUR<9~{`zz1fj^(P$(?)s1CO1yQ)v%`fb1>E+7)R(3I;ki@-OE1I~SS=jh_s559C z_4!4HJ-O7)EreZqSvAM3>6!a)cB#C(Yfqiol18ARS<~m*!XoLYsQ%p1A3Vo?9KS`A zesUC(o9m>Xf&c;tAb Date: Tue, 4 Dec 2018 14:07:03 -0500 Subject: [PATCH 05/62] Delete homeworkHub.db delete .db file --- db/homeworkHub.db | Bin 24576 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 db/homeworkHub.db diff --git a/db/homeworkHub.db b/db/homeworkHub.db deleted file mode 100644 index 5eefb5d366c32afa58691e0151471b3aa5253e38..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 24576 zcmeI#O;5rw7{KvO-Y&jf3@1YqFBl0I51++1G-N{T@=8Tm>aTHBUwTgO6JmX+7FsB2nG-_*3&Y2%k? z-l|uhHuMLDjZbUqed}oRW3#TC6a)}J009ILKmY**5I_Kd{}XTu#q!RM{h<8&p;Z1u zY-;he?K++-Jm=)X6=pdlo_prDYi3w@?wz+kUC)=wm5M!07D+w~2hn$< zu&~_s@XDIvs@-Th?T$EiJHom3E*mX9v+1_Hns|xjB=kpe7E%Wf)G%!if}!k=ef5}Z z=QUR<9~{`zz1fj^(P$(?)s1CO1yQ)v%`fb1>E+7)R(3I;ki@-OE1I~SS=jh_s559C z_4!4HJ-O7)EreZqSvAM3>6!a)cB#C(Yfqiol18ARS<~m*!XoLYsQ%p1A3Vo?9KS`A zesUC(o9m>Xf&c;tAb Date: Tue, 4 Dec 2018 14:08:22 -0500 Subject: [PATCH 06/62] Update .gitignore add .db file --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 10efe95..996f904 100644 --- a/.gitignore +++ b/.gitignore @@ -10,9 +10,12 @@ *.dll *.so *.dylib +*.db # Test binary, build with `go test -c` *.test # Output of the go coverage tool, specifically when used with LiteIDE *.out + + From 918456e3305e152f6907bede06fa06e9e2daaa01 Mon Sep 17 00:00:00 2001 From: Nathaniel Mitts <43045671+NateMitts@users.noreply.github.com> Date: Tue, 4 Dec 2018 14:10:50 -0500 Subject: [PATCH 07/62] Update .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 10efe95..89e6483 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,7 @@ *.dll *.so *.dylib +*.db # Test binary, build with `go test -c` *.test From 392db662674907422ac15ef00d54e1cfb1dd172e Mon Sep 17 00:00:00 2001 From: Robby Zambito Date: Tue, 4 Dec 2018 14:11:02 -0500 Subject: [PATCH 08/62] Added design artifacts from our Google Drive file, and updated README to reflect recent design changes. --- README.md | 14 +------------- design_artifacts/account-creation.md | 16 ++++++++++++++++ design_artifacts/database_communication.md | 13 +++++++++++++ design_artifacts/database_design.md | 20 ++++++++++++++++++++ design_artifacts/feedback_mechanism.md | 5 +++++ design_artifacts/file_uploading.md | 13 +++++++++++++ design_artifacts/post_searching.md | 9 +++++++++ 7 files changed, 77 insertions(+), 13 deletions(-) create mode 100644 design_artifacts/account-creation.md create mode 100644 design_artifacts/database_communication.md create mode 100644 design_artifacts/database_design.md create mode 100644 design_artifacts/feedback_mechanism.md create mode 100644 design_artifacts/file_uploading.md create mode 100644 design_artifacts/post_searching.md diff --git a/README.md b/README.md index 0d4da1c..13c15dd 100644 --- a/README.md +++ b/README.md @@ -13,22 +13,10 @@ Download the source using the command: git clone https://github.com/SocialHW/HomeworkHub.git ``` -Installing the required dependencies to run (such as MySQL): - -```bash -cd HomeworkHub/ -sh init/init.sh -``` +Running this project no longer requires any external dependencies to be running, such as MySQL. ## Building and Running -The project depends on the existence of a local instance of MySQL running. To start MySQL after it is installed, -run the command: - -```bash -sh init/start_db.sh -``` - To run the project as a Go script, simply run this command from the root directory of the project: ```bash diff --git a/design_artifacts/account-creation.md b/design_artifacts/account-creation.md new file mode 100644 index 0000000..4eb33e5 --- /dev/null +++ b/design_artifacts/account-creation.md @@ -0,0 +1,16 @@ +###Account Creation + +Users will be able to create an account by clicking a link in the top right corner of +the home page with the text "Register". The link will direct the user to /register where +they can provide a username, and a password. They submit the request to create an +account by pressing a button labeled "Create Account". If the username is not taken, they +will be redirected to the index page. From there they can continue to login by clicking +a link in the top right corner with the text "Login". From there they can enter the +username and password they provided before. They will be redirected to the index page, +where they will now be logged in. + +When the user clicks on the Create Account button, they will be submitting an HTTP POST +request, to /register/details with the information they provided. There will be a +handler function listening on /register/details which will query the user info table +to find an account with a matching username. If there is no account with a matching +username, the account can be created by inserting a new row into the database. \ No newline at end of file diff --git a/design_artifacts/database_communication.md b/design_artifacts/database_communication.md new file mode 100644 index 0000000..2e5b5c3 --- /dev/null +++ b/design_artifacts/database_communication.md @@ -0,0 +1,13 @@ +###Database Communication + +We should have a file at the db directory of the project that contains all of the functions needed to interact with the +database. +* There should be a RegisterUser function that will take the email and password as strings, and executes the queries to +insert the user into the database if it is a valid registration. +* There should be a LoginUser function that will take a username and password as strings, query the database to for the +password associated with the username entered. If the username exists and the password entered is equal to the password +in the database, the user is redirected to the home page. +* There should be a GetPosts function that takes a predicate function (of Post -> Boolean) and returns []Post, +containing the Posts in the database that meet the predicate. Future implementations may use a comparator function to +rank the results, such as closest search result match. +* There should be a CreatePost function, that takes a Post struct and inserts all of its fields into. diff --git a/design_artifacts/database_design.md b/design_artifacts/database_design.md new file mode 100644 index 0000000..d1ed2d2 --- /dev/null +++ b/design_artifacts/database_design.md @@ -0,0 +1,20 @@ +###Database Structure + +SQLite uses a .db file to represent the database. We should have some .db file located in the db directory. This file +will contain all of the tables we use to store information related to the site. +___ + +There should be a Posts table with the following columns: + +post_id primary key, auto inc INTEGER | username TEXT | title TEXT | file_path TEXT +___ + +There should be a Comments table with the following columns: + +post_id INTEGER | username TEXT | comment TEXT +___ + +There should be a user info table with the following columns: + +ID primary key, auto inc INTEGER | Username TEXT | password TEXT +___ \ No newline at end of file diff --git a/design_artifacts/feedback_mechanism.md b/design_artifacts/feedback_mechanism.md new file mode 100644 index 0000000..3ea5279 --- /dev/null +++ b/design_artifacts/feedback_mechanism.md @@ -0,0 +1,5 @@ +###Feedback Mechanism + +We were planning on creating some sort of rating system in order to show users more +relevant and higher quality results. We have since abandoned this idea for the sake of +time. \ No newline at end of file diff --git a/design_artifacts/file_uploading.md b/design_artifacts/file_uploading.md new file mode 100644 index 0000000..b6ca091 --- /dev/null +++ b/design_artifacts/file_uploading.md @@ -0,0 +1,13 @@ +###File Uploading + +Uploading images to the site represent the core value of the project. Users will have the ability to upload jpg/jpeg, +png, gif, and pdf files to be stored on the site. There will be a maximum upload size of 8MB in order to restrict users +from overusing the resources of the web server. Each file will be associated with a post, which is a row in the Posts +table containing information such as the user who created the post, the title, and the extension of the file. The file +will be saved in the location "posts/.". Since each post will have a unique ID, this will prevent +collisions when storing files on the disk. + +In order to create a new post, a user must be logged in and will click a link in the top right corner of the web page. +The user will be redirected to /create-post, where they will be prompted for the information required for a new post. + +It should not be allowed to use punctuation such as ;, in order to avoid SQL injection. \ No newline at end of file diff --git a/design_artifacts/post_searching.md b/design_artifacts/post_searching.md new file mode 100644 index 0000000..5c69971 --- /dev/null +++ b/design_artifacts/post_searching.md @@ -0,0 +1,9 @@ +###Post Searching + +In order to aid users in navigating the site, we will provide a searching feature. There +will be a text box located in the header of the page, where users (who do not need to be +logged in) can enter a string of text and hit enter to see the results. The search will +return results that include any of the words in the query in the title, and present +them in a vertical listing. + +It should not be allowed to use punctuation such as ;, in order to avoid SQL injection. \ No newline at end of file From 8f646f48b064dac8a3e35a4cfdfa4229270315ed Mon Sep 17 00:00:00 2001 From: Nathaniel Mitts <43045671+NateMitts@users.noreply.github.com> Date: Tue, 4 Dec 2018 14:11:41 -0500 Subject: [PATCH 09/62] Delete post_tags.go dropping post tags --- db/post_tags.go | 13 ------------- 1 file changed, 13 deletions(-) delete mode 100644 db/post_tags.go diff --git a/db/post_tags.go b/db/post_tags.go deleted file mode 100644 index 9f134b7..0000000 --- a/db/post_tags.go +++ /dev/null @@ -1,13 +0,0 @@ -package main - -import ( - "database/sql" - - _ "github.com/mattn/go-sqlite3" -) - -func main() { - database, _ := sql.Open("sqlite3", "./homeworkHub.db") - statement, _ := database.Prepare("CREATE TABLE IF NOT EXISTS tags (post_id INTEGER, tag TEXT)") - statement.Exec() -} From 5403354524bac9d4e16fcfa90c19e391fe4f6a41 Mon Sep 17 00:00:00 2001 From: Nathaniel Mitts <43045671+NateMitts@users.noreply.github.com> Date: Tue, 4 Dec 2018 14:15:22 -0500 Subject: [PATCH 10/62] Delete databaseSetAdmin.go dropping older files --- db/databaseSetAdmin.go | 28 ---------------------------- 1 file changed, 28 deletions(-) delete mode 100644 db/databaseSetAdmin.go diff --git a/db/databaseSetAdmin.go b/db/databaseSetAdmin.go deleted file mode 100644 index 486bbd2..0000000 --- a/db/databaseSetAdmin.go +++ /dev/null @@ -1,28 +0,0 @@ -package main - -import ( - "database/sql" - //"fmt" - _ "github.com/go-sql-driver/mysql" -) - -func main() { - set("homeworkHubUser") -} - -func set(name string) { - db, err := sql.Open("mysql", "root:password@/") - if err != nil { - panic(err) - } - defer db.Close() - - _, err = db.Exec("USE " + name) - if err != nil { - panic(err) - } - _, err = db.Exec("ALTER TABLE userInfo ALTER isAdmin SET DEFAULT false;") - if err != nil { - panic(err) - } -} From e68feded7ac1622aa027f6097012512b1058c6de Mon Sep 17 00:00:00 2001 From: Nathaniel Mitts <43045671+NateMitts@users.noreply.github.com> Date: Tue, 4 Dec 2018 14:15:39 -0500 Subject: [PATCH 11/62] Delete databseCreateV2.go dropping older tables --- db/databseCreateV2.go | 32 -------------------------------- 1 file changed, 32 deletions(-) delete mode 100644 db/databseCreateV2.go diff --git a/db/databseCreateV2.go b/db/databseCreateV2.go deleted file mode 100644 index 595f9fa..0000000 --- a/db/databseCreateV2.go +++ /dev/null @@ -1,32 +0,0 @@ -package main - -import ( - "database/sql" - //"fmt" - _ "github.com/go-sql-driver/mysql" -) - -func main() { - create("homeworkHubUser") -} - -func create(name string) { - db, err := sql.Open("mysql", "root:password@/") - if err != nil { - panic(err) - } - defer db.Close() - - _, err = db.Exec("CREATE DATABASE " + name) - if err != nil { - panic(err) - } - _, err = db.Exec("USE " + name) - if err != nil { - panic(err) - } - _, err = db.Exec("CREATE TABLE userInfo(user_id INTEGER NOT NULL AUTO_INCREMENT PRIMARY KEY, email VARCHAR(32) NOT NULL, username VARCHAR(32) NOT NULL, isAdmin BOOLEAN, passwordHash BINARY(60) NOT NULL);") - if err != nil { - panic(err) - } -} From 043b0e6e4319bbe8616a544465d2e847ad052ba6 Mon Sep 17 00:00:00 2001 From: Nathaniel Mitts <43045671+NateMitts@users.noreply.github.com> Date: Tue, 4 Dec 2018 14:16:00 -0500 Subject: [PATCH 12/62] Delete userTestV2.go dropping old files --- db/userTestV2.go | 52 ------------------------------------------------ 1 file changed, 52 deletions(-) delete mode 100644 db/userTestV2.go diff --git a/db/userTestV2.go b/db/userTestV2.go deleted file mode 100644 index d25750a..0000000 --- a/db/userTestV2.go +++ /dev/null @@ -1,52 +0,0 @@ -package main - -import ( - "bufio" - "database/sql" - "fmt" - _ "github.com/go-sql-driver/mysql" - "golang.org/x/crypto/bcrypt" - "log" - "os" - "strings" -) - -func main() { - userMake("homeworkHubUser") -} - -func userMake(name string) { - - reader := bufio.NewReader(os.Stdin) - // var username string - fmt.Println("Enter Username: ") - username, _ := reader.ReadString('\n') - username = strings.Replace(username, "\n", "", -1) - // var email string - fmt.Println("Enter Email: ") - email, _ := reader.ReadString('\n') - email = strings.Replace(email, "\n", "", -1) - - // var passwordhash string - fmt.Println("Enter Password: ") - password, _ := reader.ReadString('\n') - password = strings.Replace(password, "\n", "", -1) - hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) - if err != nil { - log.Fatal(err) - } - db, err := sql.Open("mysql", "root:password@/") - if err != nil { - panic(err) - } - defer db.Close() - - _, err = db.Exec("USE " + name) - if err != nil { - panic(err) - } - _, err = db.Exec(fmt.Sprintf("INSERT INTO userInfo (email,username,passwordHash) VALUES('%s', '%s', '%s');", email, username, hash)) - if err != nil { - panic(err) - } -} From b9b9682aaead92738b437409639104601ef7c9cc Mon Sep 17 00:00:00 2001 From: Nathaniel Mitts <43045671+NateMitts@users.noreply.github.com> Date: Tue, 4 Dec 2018 14:20:27 -0500 Subject: [PATCH 13/62] Delete post_tags.go deleted post tags --- db/post_tags.go | 13 ------------- 1 file changed, 13 deletions(-) delete mode 100644 db/post_tags.go diff --git a/db/post_tags.go b/db/post_tags.go deleted file mode 100644 index 9f134b7..0000000 --- a/db/post_tags.go +++ /dev/null @@ -1,13 +0,0 @@ -package main - -import ( - "database/sql" - - _ "github.com/mattn/go-sqlite3" -) - -func main() { - database, _ := sql.Open("sqlite3", "./homeworkHub.db") - statement, _ := database.Prepare("CREATE TABLE IF NOT EXISTS tags (post_id INTEGER, tag TEXT)") - statement.Exec() -} From eb5016ae3e186be9d2bf3da9bc9f58e42acfa2a6 Mon Sep 17 00:00:00 2001 From: Robby Zambito Date: Tue, 4 Dec 2018 14:40:21 -0500 Subject: [PATCH 14/62] Trimmed down Homework struct --- main.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/main.go b/main.go index 837bffb..1f005c3 100644 --- a/main.go +++ b/main.go @@ -26,7 +26,6 @@ func main() { Title: "[CS][370][Confer] First Homework", PostImage: "image1.jpeg", Comments: []string{"This post is great!", "No, it really isn't"}, - Tags: []string{"2018", "MAT", "413", "Andriamanalimanana"}, }, }, }) @@ -49,7 +48,6 @@ func main() { Title: "[CS][370][Confer] First Homework", PostImage: "image1.jpeg", Comments: []string{"This post is great!", "No, it really isn't"}, - Tags: []string{"2018", "MAT", "413", "Andriamanalimanana"}, } err := tpl.ExecuteTemplate(w, "homework.gohtml", hw) @@ -72,5 +70,4 @@ type homework struct { Title string PostImage string Comments []string - Tags []string } From dafd3bad5ce4bc5efe6584da7db65bdf844b9c21 Mon Sep 17 00:00:00 2001 From: Robby Zambito Date: Tue, 4 Dec 2018 14:55:27 -0500 Subject: [PATCH 15/62] Removed unsed init scripts --- init/databaseCreate.go | 32 -------------------------------- init/init.sh | 33 --------------------------------- init/start_db.sh | 4 ---- init/uninit.sh | 7 ------- 4 files changed, 76 deletions(-) delete mode 100644 init/databaseCreate.go delete mode 100644 init/init.sh delete mode 100644 init/start_db.sh delete mode 100644 init/uninit.sh diff --git a/init/databaseCreate.go b/init/databaseCreate.go deleted file mode 100644 index 6fe5424..0000000 --- a/init/databaseCreate.go +++ /dev/null @@ -1,32 +0,0 @@ -package main - -import ( - "database/sql" - //"fmt" - _ "github.com/go-sql-driver/mysql" -) - -func main() { - create("homeworkHub") -} - -func create(name string) { - db, err := sql.Open("mysql", "root:password@/") - if err != nil { - panic(err) - } - defer db.Close() - - _, err = db.Exec("CREATE DATABASE " + name) - if err != nil { - panic(err) - } - _, err = db.Exec("USE " + name) - if err != nil { - panic(err) - } - _, err = db.Exec("CREATE TABLE userInfo(user_id integer NOT NULL AUTO_INCREMENT PRIMARY KEY, email varchar(32) NOT NULL, username varchar(32) NOT NULL, isAdmin varchar(6), passwordHash varchar(32) NOT NULL )") - if err != nil { - panic(err) - } -} diff --git a/init/init.sh b/init/init.sh deleted file mode 100644 index 55ebdef..0000000 --- a/init/init.sh +++ /dev/null @@ -1,33 +0,0 @@ -#!/bin/sh - - -is_installed() { - search="$(apt-cache pkgnames | grep -i $1)" - - if [ -z "${search}" ]; then - return 1 - fi - - return 0 -} - - -# Install mysql-server -get_mysql() { - p="mysql-server" - - if [ ! $(is_installed $p) ]; then - # is not installed - echo "$p is not installed" - - echo "Installing... " - sudo apt update - sudo apt install mysql-server -y - - fi - - echo "Installed: " - apt-cache pkgnames | grep -i $p -} - -get_mysql diff --git a/init/start_db.sh b/init/start_db.sh deleted file mode 100644 index f55f9a8..0000000 --- a/init/start_db.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/sh - -systemctl start mysql - diff --git a/init/uninit.sh b/init/uninit.sh deleted file mode 100644 index 55fc0df..0000000 --- a/init/uninit.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/sh - -systemctl stop mysql - -sudo apt remove mysql-server - -sudo apt autoremove From 4b2c09b46e849e2cec13515846b69b468b631d33 Mon Sep 17 00:00:00 2001 From: Nathaniel Mitts Date: Wed, 5 Dec 2018 17:21:40 -0500 Subject: [PATCH 16/62] Add db function to main --- main.go | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/main.go b/main.go index 7afdba0..d94a130 100644 --- a/main.go +++ b/main.go @@ -5,9 +5,20 @@ import ( "net/http" ) +var database *sql.DB + func main() { http.Handle("/", http.FileServer(http.Dir("static/"))) log.Println("Server running...") http.ListenAndServe(":3000", nil) } +func initialize_DB() { + database, _ = sql.Open("sqlite3", "./homeworkHub.db") + statement, _ := database.Prepare("CREATE TABLE IF NOT EXISTS userInfo (id INTEGER PRIMARY KEY AUTOINCREMENT, username TEXT, password TEXT)") + statement.Exec() + statement, _ := database.Prepare("CREATE TABLE IF NOT EXISTS post_info (post_id INTEGER PRIMARY KEY AUTOINCREMENT, username TEXT, title TEXT, file_path TEXT)") + statement.Exec() + statement, _ := database.Prepare("CREATE TABLE IF NOT EXISTS comment_section (post_id INTEGER, username TEXT, comment TEXT)") + statement.Exec() +} From 75c43522bda6bd7b90a1770b6c2a364180598762 Mon Sep 17 00:00:00 2001 From: Nathaniel Mitts Date: Wed, 5 Dec 2018 17:27:31 -0500 Subject: [PATCH 17/62] added imports to main --- main.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/main.go b/main.go index d94a130..c193f20 100644 --- a/main.go +++ b/main.go @@ -1,6 +1,8 @@ package main import ( + "database/sql" + _ "github.com/mattn/go-sqlite3" "log" "net/http" ) From a19d9343be6e767075d5f6288f83df4359ad1844 Mon Sep 17 00:00:00 2001 From: Robby Zambito Date: Wed, 5 Dec 2018 17:28:53 -0500 Subject: [PATCH 18/62] Created handler and models files based on Github example --- handlers.go | 175 ++++++++++++++++++++++++++++++++++++++++++++++++++++ main.go | 24 +++---- models.go | 15 +++++ 3 files changed, 203 insertions(+), 11 deletions(-) create mode 100644 handlers.go create mode 100644 models.go diff --git a/handlers.go b/handlers.go new file mode 100644 index 0000000..96080e9 --- /dev/null +++ b/handlers.go @@ -0,0 +1,175 @@ +package main + +import ( + "database/sql" + "fmt" + "net/http" + "strconv" +) + +func registerHandler(w http.ResponseWriter, r *http.Request) { + if r.Method != "POST" { + http.ServeFile(w, r, "tmpl/register.html") + return + } + // grab user info + username := r.FormValue("username") + password := r.FormValue("password") + role := r.FormValue("role") + // Check existence of user + var user User + err := db.QueryRow("SELECT username, password, role FROM users WHERE username=?", + username).Scan(&user.Username, &user.Password, &user.Role) + switch { + // user is available + case err == sql.ErrNoRows: + hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) + checkInternalServerError(err, w) + // insert to database + _, err = db.Exec(`INSERT INTO users(username, password, role) VALUES(?, ?, ?)`, + username, hashedPassword, role) + fmt.Println("Created user: ", username) + checkInternalServerError(err, w) + case err != nil: + http.Error(w, "loi: "+err.Error(), http.StatusBadRequest) + return + default: + http.Redirect(w, r, "/login", http.StatusMovedPermanently) + } +} + +func loginHandler(w http.ResponseWriter, r *http.Request) { + if r.Method != "POST" { + http.ServeFile(w, r, "tmpl/login.html") + return + } + // grab user info from the submitted form + username := r.FormValue("usrname") + password := r.FormValue("psw") + // query database to get match username + var user User + err := db.QueryRow("SELECT username, password FROM users WHERE username=?", + username).Scan(&user.Username, &user.Password) + checkInternalServerError(err, w) + // validate password + err = bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(password)) + if err != nil { + http.Redirect(w, r, "/login", 301) + } + authenticated = true + http.Redirect(w, r, "/list", 301) + +} + +func logoutHandler(w http.ResponseWriter, r *http.Request) { + authenticated = false + isAuthenticated(w, r) +} + +func listHandler(w http.ResponseWriter, r *http.Request) { + isAuthenticated(w, r) + if r.Method != "GET" { + http.Error(w, "Method not allowed", http.StatusBadRequest) + } + rows, err := db.Query("SELECT * FROM cost") + checkInternalServerError(err, w) + var funcMap = template.FuncMap{ + "multiplication": func(n float64, f float64) float64 { + return n * f + }, + "addOne": func(n int) int { + return n + 1 + }, + } + var costs []Cost + var cost Cost + for rows.Next() { + err = rows.Scan(&cost.Id, &cost.ElectricAmount, + &cost.ElectricPrice, &cost.WaterAmount, &cost.WaterPrice, &cost.CheckedDate) + checkInternalServerError(err, w) + costs = append(costs, cost) + } + t, err := template.New("list.html").Funcs(funcMap).ParseFiles("tmpl/list.html") + checkInternalServerError(err, w) + err = t.Execute(w, costs) + checkInternalServerError(err, w) + +} + +func createHandler(w http.ResponseWriter, r *http.Request) { + isAuthenticated(w, r) + if r.Method != "POST" { + http.Redirect(w, r, "/", 301) + } + var cost Cost + cost.ElectricAmount, _ = strconv.ParseInt(r.FormValue("ElectricAmount"), 10, 64) + cost.ElectricPrice, _ = strconv.ParseFloat(r.FormValue("ElectricPrice"), 64) + cost.WaterAmount, _ = strconv.ParseInt(r.FormValue("WaterAmount"), 10, 64) + cost.WaterPrice, _ = strconv.ParseFloat(r.FormValue("WaterPrice"), 64) + cost.CheckedDate = r.FormValue("CheckedDate") + fmt.Println(cost) + + // Save to database + stmt, err := db.Prepare(` + INSERT INTO cost(electric_amount, electric_price, water_amount, water_price, checked_date) + VALUES(?, ?, ?, ?, ?) + `) + if err != nil { + fmt.Println("Prepare query error") + panic(err) + } + _, err = stmt.Exec(cost.ElectricAmount, cost.ElectricPrice, + cost.WaterAmount, cost.WaterPrice, cost.CheckedDate) + if err != nil { + fmt.Println("Execute query error") + panic(err) + } + http.Redirect(w, r, "/", 301) +} + +func updateHandler(w http.ResponseWriter, r *http.Request) { + isAuthenticated(w, r) + if r.Method != "POST" { + http.Redirect(w, r, "/", 301) + } + var cost Cost + cost.Id, _ = strconv.ParseInt(r.FormValue("Id"), 10, 64) + cost.ElectricAmount, _ = strconv.ParseInt(r.FormValue("ElectricAmount"), 10, 64) + cost.ElectricPrice, _ = strconv.ParseFloat(r.FormValue("ElectricPrice"), 64) + cost.WaterAmount, _ = strconv.ParseInt(r.FormValue("WaterAmount"), 10, 64) + cost.WaterPrice, _ = strconv.ParseFloat(r.FormValue("WaterPrice"), 64) + cost.CheckedDate = r.FormValue("CheckedDate") + fmt.Println(cost) + stmt, err := db.Prepare(` + UPDATE cost SET electric_amount=?, electric_price=?, water_amount=?, water_price=?, checked_date=? + WHERE id=? + `) + checkInternalServerError(err, w) + res, err := stmt.Exec(cost.ElectricAmount, cost.ElectricPrice, + cost.WaterAmount, cost.WaterPrice, cost.CheckedDate, cost.Id) + checkInternalServerError(err, w) + _, err = res.RowsAffected() + checkInternalServerError(err, w) + http.Redirect(w, r, "/", 301) +} + +func deleteHandler(w http.ResponseWriter, r *http.Request) { + isAuthenticated(w, r) + if r.Method != "POST" { + http.Redirect(w, r, "/", 301) + } + var costId, _ = strconv.ParseInt(r.FormValue("Id"), 10, 64) + stmt, err := db.Prepare("DELETE FROM cost WHERE id=?") + checkInternalServerError(err, w) + res, err := stmt.Exec(costId) + checkInternalServerError(err, w) + _, err = res.RowsAffected() + checkInternalServerError(err, w) + http.Redirect(w, r, "/", 301) + +} + +func indexHandler(w http.ResponseWriter, r *http.Request) { + isAuthenticated(w, r) + http.Redirect(w, r, "/list", 301) +} diff --git a/main.go b/main.go index 1f005c3..1cb2b8f 100644 --- a/main.go +++ b/main.go @@ -10,7 +10,10 @@ import ( "net/http" ) -var tpl *template.Template +var ( + tpl *template.Template + authenticated = false +) func init() { tpl = template.Must(template.ParseGlob("templates/*.gohtml")) @@ -19,8 +22,8 @@ func init() { func main() { /* Route for index page */ http.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) { - err := tpl.ExecuteTemplate(w, "index.gohtml", struct{ Posts []homework }{ - []homework{ + err := tpl.ExecuteTemplate(w, "index.gohtml", struct{ Posts []Homework }{ + []Homework{ { Id: 123, Title: "[CS][370][Confer] First Homework", @@ -43,7 +46,7 @@ func main() { /* Route for posts */ http.HandleFunc("/h/", func(w http.ResponseWriter, req *http.Request) { - hw := homework{ + hw := Homework{ Id: 123, Title: "[CS][370][Confer] First Homework", PostImage: "image1.jpeg", @@ -59,15 +62,14 @@ func main() { } }) + http.HandleFunc("/login", loginHandler) + http.HandleFunc("/logout", logoutHandler) + http.HandleFunc("/register", registerHandler) + http.HandleFunc("/list", listHandler) + http.HandleFunc("/create", createHandler) + port := ":3000" log.Printf("Server running on port %s...\n", port) http.ListenAndServe(port, nil) } - -type homework struct { - Id uint - Title string - PostImage string - Comments []string -} diff --git a/models.go b/models.go new file mode 100644 index 0000000..b9008ef --- /dev/null +++ b/models.go @@ -0,0 +1,15 @@ +package main + +type Homework struct { + Id uint + Title string + PostImage string + Comments []string +} + +type User struct { + Id int64 `json:"id"` + Username string `json:"username"` + Password string `json:"password"` + Role int64 `json:"role"` +} From 83741500f16bc4d25a09305df8454def5a589843 Mon Sep 17 00:00:00 2001 From: Nathaniel Mitts Date: Wed, 5 Dec 2018 17:33:17 -0500 Subject: [PATCH 19/62] changed statement names --- main.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/main.go b/main.go index c193f20..027d56e 100644 --- a/main.go +++ b/main.go @@ -17,10 +17,10 @@ func main() { } func initialize_DB() { database, _ = sql.Open("sqlite3", "./homeworkHub.db") - statement, _ := database.Prepare("CREATE TABLE IF NOT EXISTS userInfo (id INTEGER PRIMARY KEY AUTOINCREMENT, username TEXT, password TEXT)") - statement.Exec() - statement, _ := database.Prepare("CREATE TABLE IF NOT EXISTS post_info (post_id INTEGER PRIMARY KEY AUTOINCREMENT, username TEXT, title TEXT, file_path TEXT)") - statement.Exec() - statement, _ := database.Prepare("CREATE TABLE IF NOT EXISTS comment_section (post_id INTEGER, username TEXT, comment TEXT)") - statement.Exec() + statement1, _ := database.Prepare("CREATE TABLE IF NOT EXISTS userInfo (id INTEGER PRIMARY KEY AUTOINCREMENT, username TEXT, password TEXT)") + statement1.Exec() + statement2, _ := database.Prepare("CREATE TABLE IF NOT EXISTS post_info (post_id INTEGER PRIMARY KEY AUTOINCREMENT, username TEXT, title TEXT, file_path TEXT)") + statement2.Exec() + statement3, _ := database.Prepare("CREATE TABLE IF NOT EXISTS comment_section (post_id INTEGER, username TEXT, comment TEXT)") + statement3.Exec() } From bc0fd8e6f9db5740b60ec0919e5a61e2b338f817 Mon Sep 17 00:00:00 2001 From: Nathaniel Mitts Date: Wed, 5 Dec 2018 17:35:50 -0500 Subject: [PATCH 20/62] add function caller --- main.go | 1 + 1 file changed, 1 insertion(+) diff --git a/main.go b/main.go index 027d56e..d7c66f8 100644 --- a/main.go +++ b/main.go @@ -10,6 +10,7 @@ import ( var database *sql.DB func main() { + intitalize_DB() http.Handle("/", http.FileServer(http.Dir("static/"))) log.Println("Server running...") From c9efd12bbadfef7c754710035965bf588753100e Mon Sep 17 00:00:00 2001 From: Nathaniel Mitts Date: Wed, 5 Dec 2018 17:37:20 -0500 Subject: [PATCH 21/62] fixed spelling errors --- main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.go b/main.go index d7c66f8..2fc021b 100644 --- a/main.go +++ b/main.go @@ -10,7 +10,7 @@ import ( var database *sql.DB func main() { - intitalize_DB() + initialize_DB() http.Handle("/", http.FileServer(http.Dir("static/"))) log.Println("Server running...") From 402203312af9b7c28a4a1db1c2087adb2d6bab98 Mon Sep 17 00:00:00 2001 From: Robby Zambito Date: Wed, 5 Dec 2018 17:37:36 -0500 Subject: [PATCH 22/62] Added database variable --- main.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/main.go b/main.go index 1cb2b8f..a7bac88 100644 --- a/main.go +++ b/main.go @@ -5,6 +5,7 @@ package main import ( + "database/sql" "html/template" "log" "net/http" @@ -13,6 +14,7 @@ import ( var ( tpl *template.Template authenticated = false + database *sql.DB ) func init() { From 58ea0be85ab1f114ce0dc7c89b90e414488c5eb6 Mon Sep 17 00:00:00 2001 From: Robby Zambito Date: Wed, 5 Dec 2018 19:49:56 -0500 Subject: [PATCH 23/62] Moved all handler functions to handlers go file. Created handler function for post viewing page. Commented out related code that is not exactly what is needed. --- handlers.go | 217 ++++++++++++++++++++------------------ main.go | 51 ++------- templates/homework.gohtml | 20 ---- 3 files changed, 125 insertions(+), 163 deletions(-) diff --git a/handlers.go b/handlers.go index 96080e9..f8a8818 100644 --- a/handlers.go +++ b/handlers.go @@ -3,13 +3,13 @@ package main import ( "database/sql" "fmt" + "log" "net/http" - "strconv" ) func registerHandler(w http.ResponseWriter, r *http.Request) { if r.Method != "POST" { - http.ServeFile(w, r, "tmpl/register.html") + http.ServeFile(w, r, "templates/register.html") return } // grab user info @@ -18,16 +18,17 @@ func registerHandler(w http.ResponseWriter, r *http.Request) { role := r.FormValue("role") // Check existence of user var user User - err := db.QueryRow("SELECT username, password, role FROM users WHERE username=?", + err := database.QueryRow("SELECT username, password, role FROM users WHERE username=?", username).Scan(&user.Username, &user.Password, &user.Role) + switch { // user is available case err == sql.ErrNoRows: - hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) + //hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) checkInternalServerError(err, w) // insert to database - _, err = db.Exec(`INSERT INTO users(username, password, role) VALUES(?, ?, ?)`, - username, hashedPassword, role) + _, err = database.Exec(`INSERT INTO users(username, password, role) VALUES(?, ?, ?)`, + username, password, role) fmt.Println("Created user: ", username) checkInternalServerError(err, w) case err != nil: @@ -40,22 +41,22 @@ func registerHandler(w http.ResponseWriter, r *http.Request) { func loginHandler(w http.ResponseWriter, r *http.Request) { if r.Method != "POST" { - http.ServeFile(w, r, "tmpl/login.html") + http.ServeFile(w, r, "templates/login.html") return } - // grab user info from the submitted form - username := r.FormValue("usrname") - password := r.FormValue("psw") - // query database to get match username - var user User - err := db.QueryRow("SELECT username, password FROM users WHERE username=?", - username).Scan(&user.Username, &user.Password) - checkInternalServerError(err, w) - // validate password - err = bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(password)) - if err != nil { - http.Redirect(w, r, "/login", 301) - } + //// grab user info from the submitted form + //username := r.FormValue("usrname") + //password := r.FormValue("psw") + //// query database to get match username + //var user User + //err := database.QueryRow("SELECT username, password FROM users WHERE username=?", + // username).Scan(&user.Username, &user.Password) + //checkInternalServerError(err, w) + //// validate password + //err = bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(password)) + //if err != nil { + // http.Redirect(w, r, "/login", 301) + //} authenticated = true http.Redirect(w, r, "/list", 301) @@ -71,28 +72,28 @@ func listHandler(w http.ResponseWriter, r *http.Request) { if r.Method != "GET" { http.Error(w, "Method not allowed", http.StatusBadRequest) } - rows, err := db.Query("SELECT * FROM cost") - checkInternalServerError(err, w) - var funcMap = template.FuncMap{ - "multiplication": func(n float64, f float64) float64 { - return n * f - }, - "addOne": func(n int) int { - return n + 1 - }, - } - var costs []Cost - var cost Cost - for rows.Next() { - err = rows.Scan(&cost.Id, &cost.ElectricAmount, - &cost.ElectricPrice, &cost.WaterAmount, &cost.WaterPrice, &cost.CheckedDate) - checkInternalServerError(err, w) - costs = append(costs, cost) - } - t, err := template.New("list.html").Funcs(funcMap).ParseFiles("tmpl/list.html") - checkInternalServerError(err, w) - err = t.Execute(w, costs) - checkInternalServerError(err, w) + //rows, err := database.Query("SELECT * FROM homework") + //checkInternalServerError(err, w) + //var funcMap = tpl.FuncMap{ + // "multiplication": func(n float64, f float64) float64 { + // return n * f + // }, + // "addOne": func(n int) int { + // return n + 1 + // }, + //} + //var homeworks []Homework + //var homework Homework + //for rows.Next() { + // err = rows.Scan(&homework.Id, &homework.ElectricAmount, + // &homework.ElectricPrice, &homework.WaterAmount, &homework.WaterPrice, &homework.CheckedDate) + // checkInternalServerError(err, w) + // homeworks = append(homeworks, homework) + //} + //t, err := tpl.New("list.html").Funcs(funcMap).ParseFiles("templates/list.html") + //checkInternalServerError(err, w) + //err = t.Execute(w, homeworks) + //checkInternalServerError(err, w) } @@ -101,75 +102,89 @@ func createHandler(w http.ResponseWriter, r *http.Request) { if r.Method != "POST" { http.Redirect(w, r, "/", 301) } - var cost Cost - cost.ElectricAmount, _ = strconv.ParseInt(r.FormValue("ElectricAmount"), 10, 64) - cost.ElectricPrice, _ = strconv.ParseFloat(r.FormValue("ElectricPrice"), 64) - cost.WaterAmount, _ = strconv.ParseInt(r.FormValue("WaterAmount"), 10, 64) - cost.WaterPrice, _ = strconv.ParseFloat(r.FormValue("WaterPrice"), 64) - cost.CheckedDate = r.FormValue("CheckedDate") - fmt.Println(cost) + var homework Homework + + fmt.Println(homework) // Save to database - stmt, err := db.Prepare(` - INSERT INTO cost(electric_amount, electric_price, water_amount, water_price, checked_date) - VALUES(?, ?, ?, ?, ?) - `) - if err != nil { - fmt.Println("Prepare query error") - panic(err) - } - _, err = stmt.Exec(cost.ElectricAmount, cost.ElectricPrice, - cost.WaterAmount, cost.WaterPrice, cost.CheckedDate) - if err != nil { - fmt.Println("Execute query error") - panic(err) - } + //stmt, err := database.Prepare(` + // INSERT INTO cost(electric_amount, electric_price, water_amount, water_price, checked_date) + // VALUES(?, ?, ?, ?, ?) + //`) + + //if err != nil { + // fmt.Println("Prepare query error") + // panic(err) + //} + //_, err = stmt.Exec(cost.ElectricAmount, cost.ElectricPrice, + // cost.WaterAmount, cost.WaterPrice, cost.CheckedDate) + //if err != nil { + // fmt.Println("Execute query error") + // panic(err) + //} + http.Redirect(w, r, "/", 301) } -func updateHandler(w http.ResponseWriter, r *http.Request) { - isAuthenticated(w, r) - if r.Method != "POST" { - http.Redirect(w, r, "/", 301) +func indexHandler(w http.ResponseWriter, r *http.Request) { + if true { + posts := []Homework{ + { + Id: 123, + Title: "[CS][370][Confer] First Homework", + PostImage: "image1.jpeg", + Comments: []string{"This post is great!", "No, it really isn't"}, + }, + } + + indexData := struct { + Authenticated bool + Posts []Homework + }{ + authenticated, + posts, + } + + err := tpl.ExecuteTemplate(w, "index.gohtml", indexData) + + if err != nil { + log.Println(err) + http.Error(w, "Internal server error", http.StatusInternalServerError) + return + } + } - var cost Cost - cost.Id, _ = strconv.ParseInt(r.FormValue("Id"), 10, 64) - cost.ElectricAmount, _ = strconv.ParseInt(r.FormValue("ElectricAmount"), 10, 64) - cost.ElectricPrice, _ = strconv.ParseFloat(r.FormValue("ElectricPrice"), 64) - cost.WaterAmount, _ = strconv.ParseInt(r.FormValue("WaterAmount"), 10, 64) - cost.WaterPrice, _ = strconv.ParseFloat(r.FormValue("WaterPrice"), 64) - cost.CheckedDate = r.FormValue("CheckedDate") - fmt.Println(cost) - stmt, err := db.Prepare(` - UPDATE cost SET electric_amount=?, electric_price=?, water_amount=?, water_price=?, checked_date=? - WHERE id=? - `) - checkInternalServerError(err, w) - res, err := stmt.Exec(cost.ElectricAmount, cost.ElectricPrice, - cost.WaterAmount, cost.WaterPrice, cost.CheckedDate, cost.Id) - checkInternalServerError(err, w) - _, err = res.RowsAffected() - checkInternalServerError(err, w) - http.Redirect(w, r, "/", 301) + + //http.Redirect(w, r, "/list", 301) } -func deleteHandler(w http.ResponseWriter, r *http.Request) { - isAuthenticated(w, r) - if r.Method != "POST" { - http.Redirect(w, r, "/", 301) +func postViewHandler(w http.ResponseWriter, req *http.Request) { + + hw := Homework{ + Id: 123, + Title: "[CS][370][Confer] First Homework", + PostImage: "image1.jpeg", + Comments: []string{"This post is great!", "No, it really isn't"}, } - var costId, _ = strconv.ParseInt(r.FormValue("Id"), 10, 64) - stmt, err := db.Prepare("DELETE FROM cost WHERE id=?") - checkInternalServerError(err, w) - res, err := stmt.Exec(costId) - checkInternalServerError(err, w) - _, err = res.RowsAffected() - checkInternalServerError(err, w) - http.Redirect(w, r, "/", 301) + err := tpl.ExecuteTemplate(w, "homework.gohtml", hw) + + if err != nil { + log.Println(err) + http.Error(w, "Internal server error", http.StatusInternalServerError) + return + } +} + +func checkInternalServerError(err error, w http.ResponseWriter) { + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } } -func indexHandler(w http.ResponseWriter, r *http.Request) { - isAuthenticated(w, r) - http.Redirect(w, r, "/list", 301) +func isAuthenticated(w http.ResponseWriter, r *http.Request) { + if !authenticated { + http.Redirect(w, r, "/login", 301) + } } diff --git a/main.go b/main.go index 1fe87b3..6630e3c 100644 --- a/main.go +++ b/main.go @@ -6,10 +6,10 @@ package main import ( "database/sql" + _ "github.com/mattn/go-sqlite3" "html/template" "log" "net/http" - _ "github.com/mattn/go-sqlite3" ) var ( @@ -24,60 +24,27 @@ func init() { func main() { initialize_DB() - http.Handle("/", http.FileServer(http.Dir("static/"))) /* Route for index page */ - http.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) { - err := tpl.ExecuteTemplate(w, "index.gohtml", struct{ Posts []Homework }{ - []Homework{ - { - Id: 123, - Title: "[CS][370][Confer] First Homework", - PostImage: "image1.jpeg", - Comments: []string{"This post is great!", "No, it really isn't"}, - }, - }, - }) - - if err != nil { - log.Println(err) - http.Error(w, "Internal server error", http.StatusInternalServerError) - return - } - }) + http.HandleFunc("/", indexHandler) // Route for static assets http.Handle("/static/", http.StripPrefix("/static", http.FileServer(http.Dir("./static")))) /* Route for posts */ - http.HandleFunc("/h/", func(w http.ResponseWriter, req *http.Request) { - - hw := Homework{ - Id: 123, - Title: "[CS][370][Confer] First Homework", - PostImage: "image1.jpeg", - Comments: []string{"This post is great!", "No, it really isn't"}, - } + http.HandleFunc("/h/", postViewHandler) - err := tpl.ExecuteTemplate(w, "homework.gohtml", hw) - - if err != nil { - log.Println(err) - http.Error(w, "Internal server error", http.StatusInternalServerError) - return - } - }) - - http.HandleFunc("/login", loginHandler) - http.HandleFunc("/logout", logoutHandler) - http.HandleFunc("/register", registerHandler) - http.HandleFunc("/list", listHandler) - http.HandleFunc("/create", createHandler) + http.HandleFunc("/login/", loginHandler) + http.HandleFunc("/logout/", logoutHandler) + http.HandleFunc("/register/", registerHandler) + http.HandleFunc("/list/", listHandler) + http.HandleFunc("/create/", createHandler) port := ":3000" log.Printf("Server running on port %s...\n", port) http.ListenAndServe(port, nil) } + func initialize_DB() { database, _ = sql.Open("sqlite3", "./homeworkHub.db") statement1, _ := database.Prepare("CREATE TABLE IF NOT EXISTS userInfo (id INTEGER PRIMARY KEY AUTOINCREMENT, username TEXT, password TEXT)") diff --git a/templates/homework.gohtml b/templates/homework.gohtml index 597dc00..ed9203c 100644 --- a/templates/homework.gohtml +++ b/templates/homework.gohtml @@ -7,29 +7,9 @@

PostImage:
-
Upvotes: {{.Upvotes}}
- -
Downvotes: {{.Downvotes}}
Comments: {{range .Comments}} {{.}} {{end}}
-
- Tags: - -

From 8c1fe66c8b97b0fe11d3bf9a0f3267b3a80e0f64 Mon Sep 17 00:00:00 2001 From: Robby Zambito Date: Wed, 5 Dec 2018 20:20:06 -0500 Subject: [PATCH 24/62] Removed unnecessary field in User struct. Removed unnecessary if statement. --- handlers.go | 52 +++++++++++++++++++++++++--------------------------- models.go | 1 - 2 files changed, 25 insertions(+), 28 deletions(-) diff --git a/handlers.go b/handlers.go index f8a8818..cbef4bb 100644 --- a/handlers.go +++ b/handlers.go @@ -19,7 +19,7 @@ func registerHandler(w http.ResponseWriter, r *http.Request) { // Check existence of user var user User err := database.QueryRow("SELECT username, password, role FROM users WHERE username=?", - username).Scan(&user.Username, &user.Password, &user.Role) + username).Scan(&user.Username, &user.Password) switch { // user is available @@ -127,35 +127,33 @@ func createHandler(w http.ResponseWriter, r *http.Request) { } func indexHandler(w http.ResponseWriter, r *http.Request) { - if true { - posts := []Homework{ - { - Id: 123, - Title: "[CS][370][Confer] First Homework", - PostImage: "image1.jpeg", - Comments: []string{"This post is great!", "No, it really isn't"}, - }, - } - - indexData := struct { - Authenticated bool - Posts []Homework - }{ - authenticated, - posts, - } - - err := tpl.ExecuteTemplate(w, "index.gohtml", indexData) - - if err != nil { - log.Println(err) - http.Error(w, "Internal server error", http.StatusInternalServerError) - return - } + // TODO: Query the database to populate this array. + posts := []Homework{ + { + Id: 123, + Title: "[CS][370][Confer] First Homework", + PostImage: "image1.jpeg", + Comments: []string{"This post is great!", "No, it really isn't"}, + }, + } + + indexData := struct { + Authenticated bool + Posts []Homework + }{ + authenticated, + posts, + } + + err := tpl.ExecuteTemplate(w, "index.gohtml", indexData) + + if err != nil { + log.Println(err) + http.Error(w, "Internal server error", http.StatusInternalServerError) + return } - //http.Redirect(w, r, "/list", 301) } func postViewHandler(w http.ResponseWriter, req *http.Request) { diff --git a/models.go b/models.go index b9008ef..ed94709 100644 --- a/models.go +++ b/models.go @@ -11,5 +11,4 @@ type User struct { Id int64 `json:"id"` Username string `json:"username"` Password string `json:"password"` - Role int64 `json:"role"` } From 9cbbfc65cff6b1048b5a773b6431082cc79632f1 Mon Sep 17 00:00:00 2001 From: Robby Zambito Date: Thu, 6 Dec 2018 14:02:10 -0500 Subject: [PATCH 25/62] Removed unnecessary variables, reassigned to the same variable multiple times instead. Replaced redundant code with function call. --- handlers.go | 14 ++++---------- main.go | 15 ++++++++------- 2 files changed, 12 insertions(+), 17 deletions(-) diff --git a/handlers.go b/handlers.go index cbef4bb..05c419d 100644 --- a/handlers.go +++ b/handlers.go @@ -148,16 +148,13 @@ func indexHandler(w http.ResponseWriter, r *http.Request) { err := tpl.ExecuteTemplate(w, "index.gohtml", indexData) - if err != nil { - log.Println(err) - http.Error(w, "Internal server error", http.StatusInternalServerError) - return - } + checkInternalServerError(err, w) } func postViewHandler(w http.ResponseWriter, req *http.Request) { + // TODO: Build this struct based on the information from the database hw := Homework{ Id: 123, Title: "[CS][370][Confer] First Homework", @@ -167,15 +164,12 @@ func postViewHandler(w http.ResponseWriter, req *http.Request) { err := tpl.ExecuteTemplate(w, "homework.gohtml", hw) - if err != nil { - log.Println(err) - http.Error(w, "Internal server error", http.StatusInternalServerError) - return - } + checkInternalServerError(err, w) } func checkInternalServerError(err error, w http.ResponseWriter) { if err != nil { + log.Println(err) http.Error(w, err.Error(), http.StatusInternalServerError) return } diff --git a/main.go b/main.go index 6630e3c..3cd4513 100644 --- a/main.go +++ b/main.go @@ -24,13 +24,14 @@ func init() { func main() { initialize_DB() + /* Route for index page */ http.HandleFunc("/", indexHandler) // Route for static assets http.Handle("/static/", http.StripPrefix("/static", http.FileServer(http.Dir("./static")))) - /* Route for posts */ + // Route for posts http.HandleFunc("/h/", postViewHandler) http.HandleFunc("/login/", loginHandler) @@ -47,10 +48,10 @@ func main() { func initialize_DB() { database, _ = sql.Open("sqlite3", "./homeworkHub.db") - statement1, _ := database.Prepare("CREATE TABLE IF NOT EXISTS userInfo (id INTEGER PRIMARY KEY AUTOINCREMENT, username TEXT, password TEXT)") - statement1.Exec() - statement2, _ := database.Prepare("CREATE TABLE IF NOT EXISTS post_info (post_id INTEGER PRIMARY KEY AUTOINCREMENT, username TEXT, title TEXT, file_path TEXT)") - statement2.Exec() - statement3, _ := database.Prepare("CREATE TABLE IF NOT EXISTS comment_section (post_id INTEGER, username TEXT, comment TEXT)") - statement3.Exec() + statement, _ := database.Prepare("CREATE TABLE IF NOT EXISTS userInfo (id INTEGER PRIMARY KEY AUTOINCREMENT, username TEXT, password TEXT)") + statement.Exec() + statement, _ = database.Prepare("CREATE TABLE IF NOT EXISTS post_info (post_id INTEGER PRIMARY KEY AUTOINCREMENT, username TEXT, title TEXT, file_path TEXT)") + statement.Exec() + statement, _ = database.Prepare("CREATE TABLE IF NOT EXISTS comment_section (post_id INTEGER, username TEXT, comment TEXT)") + statement.Exec() } From b5de00e25c3ee59a465fa677fb2e63128b7fe384 Mon Sep 17 00:00:00 2001 From: Robby Zambito Date: Thu, 6 Dec 2018 14:25:23 -0500 Subject: [PATCH 26/62] Dynamically render the login/logout based on if the user is authenticated or not --- templates/top_bar.gohtml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/templates/top_bar.gohtml b/templates/top_bar.gohtml index bf042be..a473562 100644 --- a/templates/top_bar.gohtml +++ b/templates/top_bar.gohtml @@ -2,6 +2,10 @@

Social HW

- Login or Register + {{if not .Authenticated}} + Login or Register + {{else}} + Logout + {{end}}
{{end}} \ No newline at end of file From f143af4f525b354d7e4aaf7fad991c65bfd04e20 Mon Sep 17 00:00:00 2001 From: Nathaniel Mitts Date: Thu, 6 Dec 2018 14:42:34 -0500 Subject: [PATCH 27/62] Changed handlers files and dropped 'role' --- handlers.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/handlers.go b/handlers.go index 05c419d..9dee028 100644 --- a/handlers.go +++ b/handlers.go @@ -15,10 +15,10 @@ func registerHandler(w http.ResponseWriter, r *http.Request) { // grab user info username := r.FormValue("username") password := r.FormValue("password") - role := r.FormValue("role") + // Check existence of user var user User - err := database.QueryRow("SELECT username, password, role FROM users WHERE username=?", + err := database.QueryRow("SELECT username, password", username).Scan(&user.Username, &user.Password) switch { @@ -27,7 +27,7 @@ func registerHandler(w http.ResponseWriter, r *http.Request) { //hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) checkInternalServerError(err, w) // insert to database - _, err = database.Exec(`INSERT INTO users(username, password, role) VALUES(?, ?, ?)`, + _, err = database.Exec(`INSERT INTO users(username, password) VALUES(?, ?)`, username, password, role) fmt.Println("Created user: ", username) checkInternalServerError(err, w) From 38e9d78c3cf2df3618858d32567a66e9bb7d3704 Mon Sep 17 00:00:00 2001 From: Robby Zambito Date: Thu, 6 Dec 2018 14:47:02 -0500 Subject: [PATCH 28/62] Redirect logged in users to index --- handlers.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/handlers.go b/handlers.go index 05c419d..bbf6bbf 100644 --- a/handlers.go +++ b/handlers.go @@ -58,7 +58,7 @@ func loginHandler(w http.ResponseWriter, r *http.Request) { // http.Redirect(w, r, "/login", 301) //} authenticated = true - http.Redirect(w, r, "/list", 301) + http.Redirect(w, r, "/", 301) } From efbd7e6dc951eda08a16600ba15a1655d5f5ee0c Mon Sep 17 00:00:00 2001 From: Robby Zambito Date: Thu, 6 Dec 2018 16:36:02 -0500 Subject: [PATCH 29/62] Created login and register page. --- handlers.go | 56 +++++++++++++++++++++++++++------------ main.go | 2 +- templates/login.gohtml | 19 +++++++++++++ templates/register.gohtml | 25 +++++++++++++++++ 4 files changed, 84 insertions(+), 18 deletions(-) create mode 100644 templates/login.gohtml create mode 100644 templates/register.gohtml diff --git a/handlers.go b/handlers.go index bbf6bbf..825024b 100644 --- a/handlers.go +++ b/handlers.go @@ -8,14 +8,22 @@ import ( ) func registerHandler(w http.ResponseWriter, r *http.Request) { + if authenticated { + http.Redirect(w, r, "/", 301) + return + } + if r.Method != "POST" { - http.ServeFile(w, r, "templates/register.html") + err := tpl.ExecuteTemplate(w, "register.gohtml", nil) + checkInternalServerError(err, w) + return } + // grab user info username := r.FormValue("username") password := r.FormValue("password") - role := r.FormValue("role") + // Check existence of user var user User err := database.QueryRow("SELECT username, password, role FROM users WHERE username=?", @@ -28,7 +36,7 @@ func registerHandler(w http.ResponseWriter, r *http.Request) { checkInternalServerError(err, w) // insert to database _, err = database.Exec(`INSERT INTO users(username, password, role) VALUES(?, ?, ?)`, - username, password, role) + username, password) fmt.Println("Created user: ", username) checkInternalServerError(err, w) case err != nil: @@ -40,23 +48,36 @@ func registerHandler(w http.ResponseWriter, r *http.Request) { } func loginHandler(w http.ResponseWriter, r *http.Request) { + if authenticated { + http.Redirect(w, r, "/", http.StatusSeeOther) + return + } + if r.Method != "POST" { - http.ServeFile(w, r, "templates/login.html") + err := tpl.ExecuteTemplate(w, "login.gohtml", nil) + checkInternalServerError(err, w) + return } - //// grab user info from the submitted form - //username := r.FormValue("usrname") - //password := r.FormValue("psw") - //// query database to get match username - //var user User - //err := database.QueryRow("SELECT username, password FROM users WHERE username=?", - // username).Scan(&user.Username, &user.Password) - //checkInternalServerError(err, w) - //// validate password + + // grab user info from the submitted form + username := r.FormValue("usrname") + password := r.FormValue("psw") + + // query database to get match username + var user User + err := database.QueryRow("SELECT username, password FROM users WHERE username=?", + username).Scan(&user.Username, &user.Password) + + checkInternalServerError(err, w) + + // validate password //err = bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(password)) - //if err != nil { - // http.Redirect(w, r, "/login", 301) - //} + + if err != nil || password != user.Password { + http.Redirect(w, r, "/login", 301) + } + authenticated = true http.Redirect(w, r, "/", 301) @@ -72,6 +93,7 @@ func listHandler(w http.ResponseWriter, r *http.Request) { if r.Method != "GET" { http.Error(w, "Method not allowed", http.StatusBadRequest) } + //rows, err := database.Query("SELECT * FROM homework") //checkInternalServerError(err, w) //var funcMap = tpl.FuncMap{ @@ -97,7 +119,7 @@ func listHandler(w http.ResponseWriter, r *http.Request) { } -func createHandler(w http.ResponseWriter, r *http.Request) { +func newPost(w http.ResponseWriter, r *http.Request) { isAuthenticated(w, r) if r.Method != "POST" { http.Redirect(w, r, "/", 301) diff --git a/main.go b/main.go index 3cd4513..547050d 100644 --- a/main.go +++ b/main.go @@ -38,7 +38,7 @@ func main() { http.HandleFunc("/logout/", logoutHandler) http.HandleFunc("/register/", registerHandler) http.HandleFunc("/list/", listHandler) - http.HandleFunc("/create/", createHandler) + http.HandleFunc("/create/", newPost) port := ":3000" diff --git a/templates/login.gohtml b/templates/login.gohtml new file mode 100644 index 0000000..152dd5e --- /dev/null +++ b/templates/login.gohtml @@ -0,0 +1,19 @@ +{{template "header"}} + +
+ +
+ + + + + + + + Remember me +
+ +
+ + +{{template "footer"}} \ No newline at end of file diff --git a/templates/register.gohtml b/templates/register.gohtml new file mode 100644 index 0000000..70d556f --- /dev/null +++ b/templates/register.gohtml @@ -0,0 +1,25 @@ +{{template "header"}} + +
+

Register

+
+
+ + +
+ + + +
+ + + + +
+ + Login +
+
+ + +{{template "footer"}} \ No newline at end of file From 40a9a094388b2f568a108ff7616b7547748c8135 Mon Sep 17 00:00:00 2001 From: Robby Zambito Date: Thu, 6 Dec 2018 17:26:52 -0500 Subject: [PATCH 30/62] Moved register data handler to separate end point --- handlers.go | 26 ++++++++++++++++---------- main.go | 1 + templates/login.gohtml | 19 +++++-------------- templates/register.gohtml | 20 +++++++++++--------- 4 files changed, 33 insertions(+), 33 deletions(-) diff --git a/handlers.go b/handlers.go index fc0f3b7..311457e 100644 --- a/handlers.go +++ b/handlers.go @@ -8,22 +8,28 @@ import ( ) func registerHandler(w http.ResponseWriter, r *http.Request) { - if authenticated { - http.Redirect(w, r, "/", 301) - return - } - if r.Method != "POST" { + if authenticated { + http.Redirect(w, r, "/", http.StatusMovedPermanently) + return + } + err := tpl.ExecuteTemplate(w, "register.gohtml", nil) checkInternalServerError(err, w) return } +} + +func registerDataHandler(w http.ResponseWriter, r *http.Request) { + r.ParseForm() // grab user info username := r.FormValue("username") password := r.FormValue("password") + fmt.Printf("Name entered: %s \tPass entered: %s\n", username, password) + // Check existence of user var user User err := database.QueryRow("SELECT username, password", @@ -49,12 +55,12 @@ func registerHandler(w http.ResponseWriter, r *http.Request) { } func loginHandler(w http.ResponseWriter, r *http.Request) { - if authenticated { - http.Redirect(w, r, "/", http.StatusMovedPermanently) - return - } - if r.Method != "POST" { + if authenticated { + http.Redirect(w, r, "/", http.StatusMovedPermanently) + return + } + err := tpl.ExecuteTemplate(w, "login.gohtml", nil) checkInternalServerError(err, w) diff --git a/main.go b/main.go index 547050d..134bc05 100644 --- a/main.go +++ b/main.go @@ -37,6 +37,7 @@ func main() { http.HandleFunc("/login/", loginHandler) http.HandleFunc("/logout/", logoutHandler) http.HandleFunc("/register/", registerHandler) + http.HandleFunc("/register/data", registerDataHandler) http.HandleFunc("/list/", listHandler) http.HandleFunc("/create/", newPost) diff --git a/templates/login.gohtml b/templates/login.gohtml index 152dd5e..14d92de 100644 --- a/templates/login.gohtml +++ b/templates/login.gohtml @@ -1,19 +1,10 @@ {{template "header"}} -
- -
- - - - - - - - Remember me -
- -
+
+ Username: + Password: + +
{{template "footer"}} \ No newline at end of file diff --git a/templates/register.gohtml b/templates/register.gohtml index 70d556f..149754b 100644 --- a/templates/register.gohtml +++ b/templates/register.gohtml @@ -3,20 +3,22 @@

Register

-
- - + + +
- - + +
- - + + +
+ + +
-
- Login
From 6268a15df828a980185fbc8d887482763ec650bf Mon Sep 17 00:00:00 2001 From: Nathaniel Mitts Date: Thu, 6 Dec 2018 18:17:42 -0500 Subject: [PATCH 31/62] for debugging --- handlers.go | 8 ++++++-- main.go | 1 - 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/handlers.go b/handlers.go index 311457e..2e47050 100644 --- a/handlers.go +++ b/handlers.go @@ -41,8 +41,12 @@ func registerDataHandler(w http.ResponseWriter, r *http.Request) { //hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) //checkInternalServerError(err, w) // insert to database - _, err = database.Exec(`INSERT INTO users(username, password) VALUES(?, ?)`, - username, password) + err = database.Ping() + if err != nil { + panic(err) + } + statement, _ := database.Prepare("INSERT INTO userinfo(username, password) VALUES(?, ?)") + statement.Exec(username, password) fmt.Println("Created user: ", username) checkInternalServerError(err, w) diff --git a/main.go b/main.go index 134bc05..547050d 100644 --- a/main.go +++ b/main.go @@ -37,7 +37,6 @@ func main() { http.HandleFunc("/login/", loginHandler) http.HandleFunc("/logout/", logoutHandler) http.HandleFunc("/register/", registerHandler) - http.HandleFunc("/register/data", registerDataHandler) http.HandleFunc("/list/", listHandler) http.HandleFunc("/create/", newPost) From 20b1fc12fbe8fb01464035642f6bfb48da8043d0 Mon Sep 17 00:00:00 2001 From: Nathaniel Mitts Date: Thu, 6 Dec 2018 18:37:02 -0500 Subject: [PATCH 32/62] capitalized I --- handlers.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/handlers.go b/handlers.go index 2e47050..34f1395 100644 --- a/handlers.go +++ b/handlers.go @@ -45,8 +45,8 @@ func registerDataHandler(w http.ResponseWriter, r *http.Request) { if err != nil { panic(err) } - statement, _ := database.Prepare("INSERT INTO userinfo(username, password) VALUES(?, ?)") - statement.Exec(username, password) + + _, err = database.Exec("INSERT INTO userInfo(username, password) VALUES(?, ?);", username, password) fmt.Println("Created user: ", username) checkInternalServerError(err, w) From f6d327f1f0cc8f33fa83f83adcc0d04dbf57d221 Mon Sep 17 00:00:00 2001 From: Robby Zambito Date: Thu, 6 Dec 2018 21:32:10 -0500 Subject: [PATCH 33/62] For some reason the database had to be called .sqlite3. Perhaps this wasn't the best choice after all... That was two hours wasted. --- .gitignore | 3 +++ db.sqlite3 | Bin 0 -> 20480 bytes db/comment_section.go | 2 +- db/post_info.go | 2 +- db/user_info.go | 2 +- handlers.go | 35 ++++++++++++------------------ main.go | 48 ++++++++++++++++++++++++++++++------------ utils.go | 20 ++++++++++++++++++ 8 files changed, 74 insertions(+), 38 deletions(-) create mode 100644 db.sqlite3 create mode 100644 utils.go diff --git a/.gitignore b/.gitignore index 996f904..7efc66b 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,7 @@ *.so *.dylib *.db +*.sqlite3 # Test binary, build with `go test -c` *.test @@ -19,3 +20,5 @@ *.out +*.old + diff --git a/db.sqlite3 b/db.sqlite3 new file mode 100644 index 0000000000000000000000000000000000000000..b5217783a432e89da120fe5ece3726f75a13cc09 GIT binary patch literal 20480 zcmeI&K~LK-6u@ztrmGl(B5~-c!qP59R7ec%PMD&p0t%E&T2ECd?iQ_0N@K>kr+pK? zAbbFvv|%*U4%|@nx7yf_<@mi{PEX3^#Yt$i8pM-PVAO%w6jF*qrGyaW`QLU~E7_|v zAE%vsG5+7MEcP0|*6pgZu_vm3s-HLR?SO^=0tg_000IagfB*srAn?Woemj-g?yj7E zG{MzS_v6t>N2ZtPz6s-~FfD)Sc&_g$-#t3l;=u5!QmXRWr~ zdFr+8iG=`6Xoh((gK(&O%00E@kLu!6Y|BTR~fdn_C_Abzxds2&}N!Nb_yl zojZ53QmfbH)Xg55M$)qB<^9USL-M<-@t5xvd5wc4xr-;)vsBXBTvi{2{m~FW009IL zKmY**5I_I{1Q0-Ai3HwDM{I2cSN;7@2ebPBK~x`>C_z#|009ILKmY**5I_I{1Q0*~ zfi+pGujU^Sr1ii3=%0oF0tg_000IagfB*srAb)DS=b0R#|0009IL MKmY**5SR-*0r&H(_5c6? literal 0 HcmV?d00001 diff --git a/db/comment_section.go b/db/comment_section.go index 7b5e5bd..7bbfec2 100644 --- a/db/comment_section.go +++ b/db/comment_section.go @@ -7,7 +7,7 @@ import ( ) func main() { - database, _ := sql.Open("sqlite3", "./homeworkHub.db") + database, _ := sql.Open("sqlite3", "./db.sqlite3") statement, _ := database.Prepare("CREATE TABLE IF NOT EXISTS comment_section (post_id INTEGER, username TEXT, comment TEXT)") statement.Exec() } diff --git a/db/post_info.go b/db/post_info.go index 4c85b5e..5958d50 100644 --- a/db/post_info.go +++ b/db/post_info.go @@ -7,7 +7,7 @@ import ( ) func main() { - database, _ := sql.Open("sqlite3", "./homeworkHub.db") + database, _ := sql.Open("sqlite3", "./db.sqlite3") statement, _ := database.Prepare("CREATE TABLE IF NOT EXISTS post_info (post_id INTEGER PRIMARY KEY AUTOINCREMENT, username TEXT, title TEXT, file_path TEXT)") statement.Exec() } diff --git a/db/user_info.go b/db/user_info.go index 4031f79..6a53bb6 100644 --- a/db/user_info.go +++ b/db/user_info.go @@ -7,7 +7,7 @@ import ( ) func main() { - database, _ := sql.Open("sqlite3", "./homeworkHub.db") + database, _ := sql.Open("sqlite3", "./db.sqlite3") statement, _ := database.Prepare("CREATE TABLE IF NOT EXISTS userInfo (id INTEGER PRIMARY KEY AUTOINCREMENT, username TEXT, password TEXT)") statement.Exec() } diff --git a/handlers.go b/handlers.go index 34f1395..fe82da0 100644 --- a/handlers.go +++ b/handlers.go @@ -3,7 +3,6 @@ package main import ( "database/sql" "fmt" - "log" "net/http" ) @@ -19,10 +18,16 @@ func registerHandler(w http.ResponseWriter, r *http.Request) { return } + + registerDataHandler(w, r) } func registerDataHandler(w http.ResponseWriter, r *http.Request) { - r.ParseForm() + //err = r.ParseForm() + // + //if err != nil { + // panic(err) + //} // grab user info username := r.FormValue("username") @@ -32,7 +37,7 @@ func registerDataHandler(w http.ResponseWriter, r *http.Request) { // Check existence of user var user User - err := database.QueryRow("SELECT username, password", + err := database.QueryRow("SELECT username, password FROM userInfo;", username).Scan(&user.Username, &user.Password) switch { @@ -77,7 +82,7 @@ func loginHandler(w http.ResponseWriter, r *http.Request) { // query database to get match username var user User - err := database.QueryRow("SELECT username, password FROM users WHERE username=?", + err := database.QueryRow("SELECT username, password FROM userInfo WHERE username=?;", username).Scan(&user.Username, &user.Password) checkInternalServerError(err, w) @@ -133,7 +138,7 @@ func listHandler(w http.ResponseWriter, r *http.Request) { func newPost(w http.ResponseWriter, r *http.Request) { isAuthenticated(w, r) if r.Method != "POST" { - http.Redirect(w, r, "/", 301) + http.Redirect(w, r, "/", http.StatusMovedPermanently) } var homework Homework @@ -156,10 +161,10 @@ func newPost(w http.ResponseWriter, r *http.Request) { // panic(err) //} - http.Redirect(w, r, "/", 301) + http.Redirect(w, r, "/", http.StatusMovedPermanently) } -func indexHandler(w http.ResponseWriter, r *http.Request) { +func indexHandler(w http.ResponseWriter, _ *http.Request) { // TODO: Query the database to populate this array. posts := []Homework{ @@ -185,7 +190,7 @@ func indexHandler(w http.ResponseWriter, r *http.Request) { } -func postViewHandler(w http.ResponseWriter, req *http.Request) { +func postViewHandler(w http.ResponseWriter, _ *http.Request) { // TODO: Build this struct based on the information from the database hw := Homework{ @@ -199,17 +204,3 @@ func postViewHandler(w http.ResponseWriter, req *http.Request) { checkInternalServerError(err, w) } - -func checkInternalServerError(err error, w http.ResponseWriter) { - if err != nil { - log.Println(err) - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } -} - -func isAuthenticated(w http.ResponseWriter, r *http.Request) { - if !authenticated { - http.Redirect(w, r, "/login", 301) - } -} diff --git a/main.go b/main.go index 547050d..6c1505e 100644 --- a/main.go +++ b/main.go @@ -16,6 +16,7 @@ var ( tpl *template.Template authenticated = false database *sql.DB + err error ) func init() { @@ -23,7 +24,7 @@ func init() { } func main() { - initialize_DB() + initializeDb() /* Route for index page */ http.HandleFunc("/", indexHandler) @@ -32,26 +33,47 @@ func main() { http.Handle("/static/", http.StripPrefix("/static", http.FileServer(http.Dir("./static")))) // Route for posts - http.HandleFunc("/h/", postViewHandler) + http.HandleFunc("/h", postViewHandler) - http.HandleFunc("/login/", loginHandler) - http.HandleFunc("/logout/", logoutHandler) - http.HandleFunc("/register/", registerHandler) - http.HandleFunc("/list/", listHandler) - http.HandleFunc("/create/", newPost) + http.HandleFunc("/login", loginHandler) + http.HandleFunc("/logout", logoutHandler) + http.HandleFunc("/register", registerHandler) + http.HandleFunc("/create", newPost) port := ":3000" log.Printf("Server running on port %s...\n", port) - http.ListenAndServe(port, nil) + serve := http.ListenAndServe(port, nil) + + if serve != nil { + panic(serve) + } } -func initialize_DB() { - database, _ = sql.Open("sqlite3", "./homeworkHub.db") +func initializeDb() { + database, _ = sql.Open("sqlite3", "./db.sqlite3") statement, _ := database.Prepare("CREATE TABLE IF NOT EXISTS userInfo (id INTEGER PRIMARY KEY AUTOINCREMENT, username TEXT, password TEXT)") - statement.Exec() + _, err = statement.Exec() + if err != nil { + panic(err) + } + statement, _ = database.Prepare("CREATE TABLE IF NOT EXISTS post_info (post_id INTEGER PRIMARY KEY AUTOINCREMENT, username TEXT, title TEXT, file_path TEXT)") - statement.Exec() + _, err = statement.Exec() + if err != nil { + panic(err) + } + statement, _ = database.Prepare("CREATE TABLE IF NOT EXISTS comment_section (post_id INTEGER, username TEXT, comment TEXT)") - statement.Exec() + _, err = statement.Exec() + if err != nil { + panic(err) + } + + err = database.Ping() + + if err != nil { + panic(err) + } + } diff --git a/utils.go b/utils.go new file mode 100644 index 0000000..0e152f7 --- /dev/null +++ b/utils.go @@ -0,0 +1,20 @@ +package main + +import ( + "log" + "net/http" +) + +func checkInternalServerError(err error, w http.ResponseWriter) { + if err != nil { + log.Println(err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } +} + +func isAuthenticated(w http.ResponseWriter, r *http.Request) { + if !authenticated { + http.Redirect(w, r, "/login", http.StatusMovedPermanently) + } +} From 27690fc28e3bb164b0df51a71b6655576d4ae41d Mon Sep 17 00:00:00 2001 From: Robby Zambito Date: Thu, 6 Dec 2018 21:50:03 -0500 Subject: [PATCH 34/62] Can create accounts!!! --- handlers.go | 51 +++++++-------------------------------- templates/index.gohtml | 2 +- templates/register.gohtml | 6 ++--- 3 files changed, 13 insertions(+), 46 deletions(-) diff --git a/handlers.go b/handlers.go index fe82da0..6bee150 100644 --- a/handlers.go +++ b/handlers.go @@ -7,6 +7,8 @@ import ( ) func registerHandler(w http.ResponseWriter, r *http.Request) { + fmt.Printf("Register method: %s\n", r.Method) + if r.Method != "POST" { if authenticated { http.Redirect(w, r, "/", http.StatusMovedPermanently) @@ -19,16 +21,6 @@ func registerHandler(w http.ResponseWriter, r *http.Request) { return } - registerDataHandler(w, r) -} - -func registerDataHandler(w http.ResponseWriter, r *http.Request) { - //err = r.ParseForm() - // - //if err != nil { - // panic(err) - //} - // grab user info username := r.FormValue("username") password := r.FormValue("password") @@ -64,6 +56,9 @@ func registerDataHandler(w http.ResponseWriter, r *http.Request) { } func loginHandler(w http.ResponseWriter, r *http.Request) { + + fmt.Printf("login method: %s\n", r.Method) + if r.Method != "POST" { if authenticated { http.Redirect(w, r, "/", http.StatusMovedPermanently) @@ -77,8 +72,8 @@ func loginHandler(w http.ResponseWriter, r *http.Request) { } // grab user info from the submitted form - username := r.FormValue("usrname") - password := r.FormValue("psw") + username := r.FormValue("username") + password := r.FormValue("password") // query database to get match username var user User @@ -104,39 +99,11 @@ func logoutHandler(w http.ResponseWriter, r *http.Request) { isAuthenticated(w, r) } -func listHandler(w http.ResponseWriter, r *http.Request) { +func newPost(w http.ResponseWriter, r *http.Request) { isAuthenticated(w, r) - if r.Method != "GET" { - http.Error(w, "Method not allowed", http.StatusBadRequest) - } - //rows, err := database.Query("SELECT * FROM homework") - //checkInternalServerError(err, w) - //var funcMap = tpl.FuncMap{ - // "multiplication": func(n float64, f float64) float64 { - // return n * f - // }, - // "addOne": func(n int) int { - // return n + 1 - // }, - //} - //var homeworks []Homework - //var homework Homework - //for rows.Next() { - // err = rows.Scan(&homework.Id, &homework.ElectricAmount, - // &homework.ElectricPrice, &homework.WaterAmount, &homework.WaterPrice, &homework.CheckedDate) - // checkInternalServerError(err, w) - // homeworks = append(homeworks, homework) - //} - //t, err := tpl.New("list.html").Funcs(funcMap).ParseFiles("templates/list.html") - //checkInternalServerError(err, w) - //err = t.Execute(w, homeworks) - //checkInternalServerError(err, w) + fmt.Printf("new post method: %s\n", r.Method) -} - -func newPost(w http.ResponseWriter, r *http.Request) { - isAuthenticated(w, r) if r.Method != "POST" { http.Redirect(w, r, "/", http.StatusMovedPermanently) } diff --git a/templates/index.gohtml b/templates/index.gohtml index 5258922..0cd7c8f 100644 --- a/templates/index.gohtml +++ b/templates/index.gohtml @@ -1,7 +1,7 @@ {{template "header"}}
- {{template "top_bar"}} + {{template "top_bar" .}}
{{range .Posts}} {{template "post_listing" .}} {{end}} diff --git a/templates/register.gohtml b/templates/register.gohtml index 149754b..a8a9b55 100644 --- a/templates/register.gohtml +++ b/templates/register.gohtml @@ -3,12 +3,12 @@

Register

-
- + +
- +
From 363ef1a1cb67a67ac8a6fa5eea608471b3eb8ac0 Mon Sep 17 00:00:00 2001 From: Robby Zambito Date: Thu, 6 Dec 2018 22:07:40 -0500 Subject: [PATCH 35/62] Can create accounts!!! ... kind of --- db.sqlite3 | Bin 20480 -> 0 bytes handlers.go | 11 ++++++----- 2 files changed, 6 insertions(+), 5 deletions(-) delete mode 100644 db.sqlite3 diff --git a/db.sqlite3 b/db.sqlite3 deleted file mode 100644 index b5217783a432e89da120fe5ece3726f75a13cc09..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20480 zcmeI&K~LK-6u@ztrmGl(B5~-c!qP59R7ec%PMD&p0t%E&T2ECd?iQ_0N@K>kr+pK? zAbbFvv|%*U4%|@nx7yf_<@mi{PEX3^#Yt$i8pM-PVAO%w6jF*qrGyaW`QLU~E7_|v zAE%vsG5+7MEcP0|*6pgZu_vm3s-HLR?SO^=0tg_000IagfB*srAn?Woemj-g?yj7E zG{MzS_v6t>N2ZtPz6s-~FfD)Sc&_g$-#t3l;=u5!QmXRWr~ zdFr+8iG=`6Xoh((gK(&O%00E@kLu!6Y|BTR~fdn_C_Abzxds2&}N!Nb_yl zojZ53QmfbH)Xg55M$)qB<^9USL-M<-@t5xvd5wc4xr-;)vsBXBTvi{2{m~FW009IL zKmY**5I_I{1Q0-Ai3HwDM{I2cSN;7@2ebPBK~x`>C_z#|009ILKmY**5I_I{1Q0*~ zfi+pGujU^Sr1ii3=%0oF0tg_000IagfB*srAb)DS=b0R#|0009IL MKmY**5SR-*0r&H(_5c6? diff --git a/handlers.go b/handlers.go index 6bee150..bba7a72 100644 --- a/handlers.go +++ b/handlers.go @@ -3,6 +3,7 @@ package main import ( "database/sql" "fmt" + "golang.org/x/crypto/bcrypt" "net/http" ) @@ -25,7 +26,7 @@ func registerHandler(w http.ResponseWriter, r *http.Request) { username := r.FormValue("username") password := r.FormValue("password") - fmt.Printf("Name entered: %s \tPass entered: %s\n", username, password) + fmt.Printf("Name entered: %s\tPass entered: %s\n", username, password) // Check existence of user var user User @@ -35,7 +36,7 @@ func registerHandler(w http.ResponseWriter, r *http.Request) { switch { // user is available case err == sql.ErrNoRows: - //hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) + hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) //checkInternalServerError(err, w) // insert to database err = database.Ping() @@ -43,7 +44,7 @@ func registerHandler(w http.ResponseWriter, r *http.Request) { panic(err) } - _, err = database.Exec("INSERT INTO userInfo(username, password) VALUES(?, ?);", username, password) + _, err = database.Exec("INSERT INTO userInfo(username, password) VALUES(?, ?);", username, hashedPassword) fmt.Println("Created user: ", username) checkInternalServerError(err, w) @@ -83,9 +84,9 @@ func loginHandler(w http.ResponseWriter, r *http.Request) { checkInternalServerError(err, w) // validate password - //err = bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(password)) + err = bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(password)) - if err != nil || password != user.Password { + if err != nil { http.Redirect(w, r, "/login", http.StatusMovedPermanently) } From 8aa5775c66e989a82e14558bec99b48b083eabe6 Mon Sep 17 00:00:00 2001 From: Robby Zambito Date: Thu, 6 Dec 2018 22:16:46 -0500 Subject: [PATCH 36/62] Can create accounts for real this time!! --- handlers.go | 17 ++--------------- utils.go | 2 +- 2 files changed, 3 insertions(+), 16 deletions(-) diff --git a/handlers.go b/handlers.go index bba7a72..ccf3506 100644 --- a/handlers.go +++ b/handlers.go @@ -11,10 +11,6 @@ func registerHandler(w http.ResponseWriter, r *http.Request) { fmt.Printf("Register method: %s\n", r.Method) if r.Method != "POST" { - if authenticated { - http.Redirect(w, r, "/", http.StatusMovedPermanently) - return - } err := tpl.ExecuteTemplate(w, "register.gohtml", nil) checkInternalServerError(err, w) @@ -30,19 +26,15 @@ func registerHandler(w http.ResponseWriter, r *http.Request) { // Check existence of user var user User - err := database.QueryRow("SELECT username, password FROM userInfo;", + err := database.QueryRow("SELECT username, password FROM userInfo WHERE username=?;", username).Scan(&user.Username, &user.Password) switch { // user is available case err == sql.ErrNoRows: hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) - //checkInternalServerError(err, w) + checkInternalServerError(err, w) // insert to database - err = database.Ping() - if err != nil { - panic(err) - } _, err = database.Exec("INSERT INTO userInfo(username, password) VALUES(?, ?);", username, hashedPassword) @@ -61,11 +53,6 @@ func loginHandler(w http.ResponseWriter, r *http.Request) { fmt.Printf("login method: %s\n", r.Method) if r.Method != "POST" { - if authenticated { - http.Redirect(w, r, "/", http.StatusMovedPermanently) - return - } - err := tpl.ExecuteTemplate(w, "login.gohtml", nil) checkInternalServerError(err, w) diff --git a/utils.go b/utils.go index 0e152f7..8dcc7f4 100644 --- a/utils.go +++ b/utils.go @@ -15,6 +15,6 @@ func checkInternalServerError(err error, w http.ResponseWriter) { func isAuthenticated(w http.ResponseWriter, r *http.Request) { if !authenticated { - http.Redirect(w, r, "/login", http.StatusMovedPermanently) + http.Redirect(w, r, "/", http.StatusMovedPermanently) } } From df60abc0076ed9152a59b0f65255c23632d3eaab Mon Sep 17 00:00:00 2001 From: Robby Zambito Date: Thu, 6 Dec 2018 22:20:55 -0500 Subject: [PATCH 37/62] Reformated code --- main.go | 6 ++++++ static/css/style.css | 16 ++++++++-------- templates/homework.gohtml | 14 +++++++------- templates/includes.gohtml | 30 +++++++++++++++++++----------- 4 files changed, 40 insertions(+), 26 deletions(-) diff --git a/main.go b/main.go index 6c1505e..1e4603e 100644 --- a/main.go +++ b/main.go @@ -25,6 +25,12 @@ func init() { func main() { initializeDb() + defer func() { + err = database.Close() + if err != nil { + panic(err) + } + }() /* Route for index page */ http.HandleFunc("/", indexHandler) diff --git a/static/css/style.css b/static/css/style.css index 206cf74..ab48601 100644 --- a/static/css/style.css +++ b/static/css/style.css @@ -1,32 +1,32 @@ body { - margin-top: 2em; + margin-top: 2em; font-family: 'Nanum Gothic', sans-serif; } .post { - padding-top: 1em; + padding-top: 1em; } a { - color: black; + color: black; } .preview { - max-width: 5em; + max-width: 5em; } .preview:hover { - max-width: 100%; + max-width: 100%; } #account-prompt { - float: right; + float: right; } .header > h1 { - float: left; + float: left; } .header { - display: flow-root; + display: flow-root; } \ No newline at end of file diff --git a/templates/homework.gohtml b/templates/homework.gohtml index ed9203c..5af63f0 100644 --- a/templates/homework.gohtml +++ b/templates/homework.gohtml @@ -1,18 +1,18 @@ {{template "header"}} -
+
- {{template "top_bar"}} + {{template "top_bar"}} -

-

PostImage:
+

+

PostImage:
-
Comments: {{range .Comments}} {{.}} {{end}}
+
Comments: {{range .Comments}} {{.}} {{end}}
-

+

-
+
{{template "footer"}} \ No newline at end of file diff --git a/templates/includes.gohtml b/templates/includes.gohtml index bc9ea56..99185fb 100644 --- a/templates/includes.gohtml +++ b/templates/includes.gohtml @@ -1,21 +1,29 @@ {{define "header"}} - - - HomeworkHub + + + HomeworkHub - - - - - + + + + + {{end}} {{define "footer"}} - - - + + + {{end}} \ No newline at end of file From dfc6be0ca87b8a6c4a74f648e957719b0f94c8d5 Mon Sep 17 00:00:00 2001 From: Robby Zambito Date: Thu, 6 Dec 2018 22:45:31 -0500 Subject: [PATCH 38/62] Can upload files! --- handlers.go | 48 +++++++++++++++++++++++++++++++++-------- main.go | 2 +- posts/.gitignore | 5 +++++ templates/upload.gohtml | 11 ++++++++++ utils.go | 2 +- 5 files changed, 57 insertions(+), 11 deletions(-) create mode 100644 posts/.gitignore create mode 100644 templates/upload.gohtml diff --git a/handlers.go b/handlers.go index ccf3506..6a5cd63 100644 --- a/handlers.go +++ b/handlers.go @@ -4,7 +4,9 @@ import ( "database/sql" "fmt" "golang.org/x/crypto/bcrypt" + "io" "net/http" + "os" ) func registerHandler(w http.ResponseWriter, r *http.Request) { @@ -44,7 +46,7 @@ func registerHandler(w http.ResponseWriter, r *http.Request) { http.Error(w, "loi: "+err.Error(), http.StatusBadRequest) return default: - http.Redirect(w, r, "/login", http.StatusMovedPermanently) + http.Redirect(w, r, "/login", http.StatusTemporaryRedirect) } } @@ -74,11 +76,11 @@ func loginHandler(w http.ResponseWriter, r *http.Request) { err = bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(password)) if err != nil { - http.Redirect(w, r, "/login", http.StatusMovedPermanently) + http.Redirect(w, r, "/login", http.StatusTemporaryRedirect) } authenticated = true - http.Redirect(w, r, "/", http.StatusMovedPermanently) + http.Redirect(w, r, "/", http.StatusTemporaryRedirect) } @@ -87,16 +89,44 @@ func logoutHandler(w http.ResponseWriter, r *http.Request) { isAuthenticated(w, r) } -func newPost(w http.ResponseWriter, r *http.Request) { +func uploadHandler(w http.ResponseWriter, r *http.Request) { isAuthenticated(w, r) - fmt.Printf("new post method: %s\n", r.Method) + fmt.Printf("Upload method: %s\n", r.Method) - if r.Method != "POST" { - http.Redirect(w, r, "/", http.StatusMovedPermanently) + if r.Method == "GET" { + + err := tpl.ExecuteTemplate(w, "upload.gohtml", nil) + checkInternalServerError(err, w) + + return } - var homework Homework + err = r.ParseMultipartForm(32 << 20) + + if err != nil { + panic(err) + } + + file, handler, err := r.FormFile("upload-file") + + if err != nil { + panic(err) + } + + defer file.Close() + + f, err := os.OpenFile("./posts/"+handler.Filename, os.O_WRONLY|os.O_CREATE, 0666) + + if err != nil { + panic(err) + return + } + + defer f.Close() + io.Copy(f, file) + + var homework Homework fmt.Println(homework) // Save to database @@ -116,7 +146,7 @@ func newPost(w http.ResponseWriter, r *http.Request) { // panic(err) //} - http.Redirect(w, r, "/", http.StatusMovedPermanently) + http.Redirect(w, r, "/", http.StatusTemporaryRedirect) } func indexHandler(w http.ResponseWriter, _ *http.Request) { diff --git a/main.go b/main.go index 1e4603e..902a30d 100644 --- a/main.go +++ b/main.go @@ -44,7 +44,7 @@ func main() { http.HandleFunc("/login", loginHandler) http.HandleFunc("/logout", logoutHandler) http.HandleFunc("/register", registerHandler) - http.HandleFunc("/create", newPost) + http.HandleFunc("/upload", uploadHandler) port := ":3000" diff --git a/posts/.gitignore b/posts/.gitignore new file mode 100644 index 0000000..57ffd0e --- /dev/null +++ b/posts/.gitignore @@ -0,0 +1,5 @@ +*.jpg +*.png +*.pdf +*.jpeg +*.gif \ No newline at end of file diff --git a/templates/upload.gohtml b/templates/upload.gohtml new file mode 100644 index 0000000..5f9199c --- /dev/null +++ b/templates/upload.gohtml @@ -0,0 +1,11 @@ +{{template "header"}} + + + + + + + + + +{{template "footer"}} \ No newline at end of file diff --git a/utils.go b/utils.go index 8dcc7f4..a442c39 100644 --- a/utils.go +++ b/utils.go @@ -15,6 +15,6 @@ func checkInternalServerError(err error, w http.ResponseWriter) { func isAuthenticated(w http.ResponseWriter, r *http.Request) { if !authenticated { - http.Redirect(w, r, "/", http.StatusMovedPermanently) + http.Redirect(w, r, "/", http.StatusTemporaryRedirect) } } From b496bd7c6b704982a1be814c9a3d6358dc3893a6 Mon Sep 17 00:00:00 2001 From: Robby Zambito Date: Thu, 6 Dec 2018 23:27:16 -0500 Subject: [PATCH 39/62] Can upload files with unique names --- design_artifacts/database_design.md | 2 +- handlers.go | 51 ++++++++++++++++++----------- main.go | 4 +-- models.go | 9 ++++- 4 files changed, 43 insertions(+), 23 deletions(-) diff --git a/design_artifacts/database_design.md b/design_artifacts/database_design.md index d1ed2d2..b8a4aba 100644 --- a/design_artifacts/database_design.md +++ b/design_artifacts/database_design.md @@ -6,7 +6,7 @@ ___ There should be a Posts table with the following columns: -post_id primary key, auto inc INTEGER | username TEXT | title TEXT | file_path TEXT +post_id primary key, auto inc INTEGER | username TEXT | title TEXT | extension TEXT ___ There should be a Comments table with the following columns: diff --git a/handlers.go b/handlers.go index 6a5cd63..de317f8 100644 --- a/handlers.go +++ b/handlers.go @@ -5,12 +5,12 @@ import ( "fmt" "golang.org/x/crypto/bcrypt" "io" + "log" "net/http" "os" ) func registerHandler(w http.ResponseWriter, r *http.Request) { - fmt.Printf("Register method: %s\n", r.Method) if r.Method != "POST" { @@ -52,8 +52,6 @@ func registerHandler(w http.ResponseWriter, r *http.Request) { func loginHandler(w http.ResponseWriter, r *http.Request) { - fmt.Printf("login method: %s\n", r.Method) - if r.Method != "POST" { err := tpl.ExecuteTemplate(w, "login.gohtml", nil) checkInternalServerError(err, w) @@ -75,9 +73,7 @@ func loginHandler(w http.ResponseWriter, r *http.Request) { // validate password err = bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(password)) - if err != nil { - http.Redirect(w, r, "/login", http.StatusTemporaryRedirect) - } + checkInternalServerError(err, w) authenticated = true http.Redirect(w, r, "/", http.StatusTemporaryRedirect) @@ -92,10 +88,7 @@ func logoutHandler(w http.ResponseWriter, r *http.Request) { func uploadHandler(w http.ResponseWriter, r *http.Request) { isAuthenticated(w, r) - fmt.Printf("Upload method: %s\n", r.Method) - if r.Method == "GET" { - err := tpl.ExecuteTemplate(w, "upload.gohtml", nil) checkInternalServerError(err, w) @@ -104,30 +97,50 @@ func uploadHandler(w http.ResponseWriter, r *http.Request) { err = r.ParseMultipartForm(32 << 20) - if err != nil { - panic(err) - } + checkInternalServerError(err, w) + + var post Post file, handler, err := r.FormFile("upload-file") if err != nil { panic(err) + return + } + + rows, err := database.Query("SELECT COUNT(*) FROM postInfo") + checkInternalServerError(err, w) + defer rows.Close() + var count int + for rows.Next() { + if err := rows.Scan(&count); err != nil { + log.Fatal(err) + } } + post.Id = count + 1 + + post.Title = r.FormValue("title") + + log.Printf("ID: %d\n", post.Id) + + checkInternalServerError(err, w) + defer file.Close() - f, err := os.OpenFile("./posts/"+handler.Filename, os.O_WRONLY|os.O_CREATE, 0666) + filename := fmt.Sprintf("%d%s", post.Id, handler.Filename) - if err != nil { - panic(err) - return - } + _, err = database.Exec("INSERT INTO postInfo(username, title, extension) VALUES(?, ?, ?);", + post.Username, post.Title, post.Extension) + + f, err := os.OpenFile("./posts/"+filename, os.O_WRONLY|os.O_CREATE, 0666) + + checkInternalServerError(err, w) defer f.Close() io.Copy(f, file) - var homework Homework - fmt.Println(homework) + fmt.Println(post) // Save to database //stmt, err := database.Prepare(` diff --git a/main.go b/main.go index 902a30d..46dd27d 100644 --- a/main.go +++ b/main.go @@ -64,13 +64,13 @@ func initializeDb() { panic(err) } - statement, _ = database.Prepare("CREATE TABLE IF NOT EXISTS post_info (post_id INTEGER PRIMARY KEY AUTOINCREMENT, username TEXT, title TEXT, file_path TEXT)") + statement, _ = database.Prepare("CREATE TABLE IF NOT EXISTS postInfo (postId INTEGER PRIMARY KEY AUTOINCREMENT, username TEXT, title TEXT, extension TEXT)") _, err = statement.Exec() if err != nil { panic(err) } - statement, _ = database.Prepare("CREATE TABLE IF NOT EXISTS comment_section (post_id INTEGER, username TEXT, comment TEXT)") + statement, _ = database.Prepare("CREATE TABLE IF NOT EXISTS commentSection (postId INTEGER, username TEXT, comment TEXT)") _, err = statement.Exec() if err != nil { panic(err) diff --git a/models.go b/models.go index ed94709..dec7404 100644 --- a/models.go +++ b/models.go @@ -8,7 +8,14 @@ type Homework struct { } type User struct { - Id int64 `json:"id"` + Id int `json:"id"` Username string `json:"username"` Password string `json:"password"` } + +type Post struct { + Id int `json:"id"` + Username string `json:"username"` + Title string `json:"title"` + Extension string `json:"title"` +} From 685ada8c89d931fc8868bfb86e48fe348020c1b9 Mon Sep 17 00:00:00 2001 From: Robby Zambito Date: Thu, 6 Dec 2018 23:34:39 -0500 Subject: [PATCH 40/62] Create files in the form of "id.ext" --- handlers.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/handlers.go b/handlers.go index de317f8..c7c6e3f 100644 --- a/handlers.go +++ b/handlers.go @@ -8,6 +8,7 @@ import ( "log" "net/http" "os" + "regexp" ) func registerHandler(w http.ResponseWriter, r *http.Request) { @@ -128,7 +129,9 @@ func uploadHandler(w http.ResponseWriter, r *http.Request) { defer file.Close() - filename := fmt.Sprintf("%d%s", post.Id, handler.Filename) + reg, _ := regexp.Compile("\\.[0-9a-z]{1,5}$") + + filename := fmt.Sprintf("%d%s", post.Id, reg.Find([]byte(handler.Filename))) _, err = database.Exec("INSERT INTO postInfo(username, title, extension) VALUES(?, ?, ?);", post.Username, post.Title, post.Extension) From 7fefb2ca3b0bc02af598d1b1da2c45170c492d48 Mon Sep 17 00:00:00 2001 From: Robby Zambito Date: Thu, 6 Dec 2018 23:38:04 -0500 Subject: [PATCH 41/62] Removed old comment --- handlers.go | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/handlers.go b/handlers.go index c7c6e3f..4c36869 100644 --- a/handlers.go +++ b/handlers.go @@ -141,27 +141,11 @@ func uploadHandler(w http.ResponseWriter, r *http.Request) { checkInternalServerError(err, w) defer f.Close() - io.Copy(f, file) + _, err = io.Copy(f, file) + checkInternalServerError(err, w) fmt.Println(post) - // Save to database - //stmt, err := database.Prepare(` - // INSERT INTO cost(electric_amount, electric_price, water_amount, water_price, checked_date) - // VALUES(?, ?, ?, ?, ?) - //`) - - //if err != nil { - // fmt.Println("Prepare query error") - // panic(err) - //} - //_, err = stmt.Exec(cost.ElectricAmount, cost.ElectricPrice, - // cost.WaterAmount, cost.WaterPrice, cost.CheckedDate) - //if err != nil { - // fmt.Println("Execute query error") - // panic(err) - //} - http.Redirect(w, r, "/", http.StatusTemporaryRedirect) } From b33daaa9029a28c673ddcf2775fddde342bd7d87 Mon Sep 17 00:00:00 2001 From: Robby Zambito Date: Thu, 6 Dec 2018 23:38:55 -0500 Subject: [PATCH 42/62] Updated formatting --- templates/upload.gohtml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/upload.gohtml b/templates/upload.gohtml index 5f9199c..6a36f6c 100644 --- a/templates/upload.gohtml +++ b/templates/upload.gohtml @@ -3,9 +3,9 @@
- + - +
{{template "footer"}} \ No newline at end of file From 9a97ffe36ee2a079043100a9c71b658bfe7b41bb Mon Sep 17 00:00:00 2001 From: Robby Zambito Date: Fri, 7 Dec 2018 00:10:47 -0500 Subject: [PATCH 43/62] Successfully storing all data related to post when uploading. --- handlers.go | 24 ++++++++++++++---------- main.go | 5 +++-- models.go | 1 - utils.go | 2 +- 4 files changed, 18 insertions(+), 14 deletions(-) diff --git a/handlers.go b/handlers.go index 4c36869..9a3bac8 100644 --- a/handlers.go +++ b/handlers.go @@ -47,7 +47,7 @@ func registerHandler(w http.ResponseWriter, r *http.Request) { http.Error(w, "loi: "+err.Error(), http.StatusBadRequest) return default: - http.Redirect(w, r, "/login", http.StatusTemporaryRedirect) + http.Redirect(w, r, "/login", http.StatusMovedPermanently) } } @@ -65,7 +65,6 @@ func loginHandler(w http.ResponseWriter, r *http.Request) { password := r.FormValue("password") // query database to get match username - var user User err := database.QueryRow("SELECT username, password FROM userInfo WHERE username=?;", username).Scan(&user.Username, &user.Password) @@ -77,7 +76,8 @@ func loginHandler(w http.ResponseWriter, r *http.Request) { checkInternalServerError(err, w) authenticated = true - http.Redirect(w, r, "/", http.StatusTemporaryRedirect) + + http.Redirect(w, r, "/", http.StatusMovedPermanently) } @@ -102,14 +102,14 @@ func uploadHandler(w http.ResponseWriter, r *http.Request) { var post Post + post.Username = user.Username + file, handler, err := r.FormFile("upload-file") - if err != nil { - panic(err) - return - } + checkInternalServerError(err, w) rows, err := database.Query("SELECT COUNT(*) FROM postInfo") + checkInternalServerError(err, w) defer rows.Close() var count int @@ -131,7 +131,9 @@ func uploadHandler(w http.ResponseWriter, r *http.Request) { reg, _ := regexp.Compile("\\.[0-9a-z]{1,5}$") - filename := fmt.Sprintf("%d%s", post.Id, reg.Find([]byte(handler.Filename))) + post.Extension = string(reg.Find([]byte(handler.Filename))) + + filename := fmt.Sprintf("%d%s", post.Id, post.Extension) _, err = database.Exec("INSERT INTO postInfo(username, title, extension) VALUES(?, ?, ?);", post.Username, post.Title, post.Extension) @@ -146,7 +148,7 @@ func uploadHandler(w http.ResponseWriter, r *http.Request) { fmt.Println(post) - http.Redirect(w, r, "/", http.StatusTemporaryRedirect) + http.Redirect(w, r, "/", http.StatusMovedPermanently) } func indexHandler(w http.ResponseWriter, _ *http.Request) { @@ -175,7 +177,9 @@ func indexHandler(w http.ResponseWriter, _ *http.Request) { } -func postViewHandler(w http.ResponseWriter, _ *http.Request) { +func postViewHandler(w http.ResponseWriter, r *http.Request) { + + fmt.Println(r.RequestURI) // TODO: Build this struct based on the information from the database hw := Homework{ diff --git a/main.go b/main.go index 46dd27d..f2a568a 100644 --- a/main.go +++ b/main.go @@ -17,6 +17,7 @@ var ( authenticated = false database *sql.DB err error + user User ) func init() { @@ -32,14 +33,14 @@ func main() { } }() - /* Route for index page */ + // Route for index page http.HandleFunc("/", indexHandler) // Route for static assets http.Handle("/static/", http.StripPrefix("/static", http.FileServer(http.Dir("./static")))) // Route for posts - http.HandleFunc("/h", postViewHandler) + http.HandleFunc("/h/*", postViewHandler) http.HandleFunc("/login", loginHandler) http.HandleFunc("/logout", logoutHandler) diff --git a/models.go b/models.go index dec7404..8bcf552 100644 --- a/models.go +++ b/models.go @@ -8,7 +8,6 @@ type Homework struct { } type User struct { - Id int `json:"id"` Username string `json:"username"` Password string `json:"password"` } diff --git a/utils.go b/utils.go index a442c39..8dcc7f4 100644 --- a/utils.go +++ b/utils.go @@ -15,6 +15,6 @@ func checkInternalServerError(err error, w http.ResponseWriter) { func isAuthenticated(w http.ResponseWriter, r *http.Request) { if !authenticated { - http.Redirect(w, r, "/", http.StatusTemporaryRedirect) + http.Redirect(w, r, "/", http.StatusMovedPermanently) } } From 3117422214877df1b9b3e7c65d5f6851d345ccd5 Mon Sep 17 00:00:00 2001 From: Robby Zambito Date: Fri, 7 Dec 2018 01:58:33 -0500 Subject: [PATCH 44/62] Can navigate to the id of the image at /h/# --- handlers.go | 30 ++++++++++++++++++------------ main.go | 3 ++- models.go | 4 ++-- templates/homework.gohtml | 2 +- templates/post_listing.gohtml | 2 +- utils.go | 2 +- 6 files changed, 25 insertions(+), 18 deletions(-) diff --git a/handlers.go b/handlers.go index 9a3bac8..e81853f 100644 --- a/handlers.go +++ b/handlers.go @@ -9,6 +9,8 @@ import ( "net/http" "os" "regexp" + "strconv" + "strings" ) func registerHandler(w http.ResponseWriter, r *http.Request) { @@ -41,8 +43,8 @@ func registerHandler(w http.ResponseWriter, r *http.Request) { _, err = database.Exec("INSERT INTO userInfo(username, password) VALUES(?, ?);", username, hashedPassword) - fmt.Println("Created user: ", username) checkInternalServerError(err, w) + http.Redirect(w, r, "/login", http.StatusMovedPermanently) case err != nil: http.Error(w, "loi: "+err.Error(), http.StatusBadRequest) return @@ -112,7 +114,7 @@ func uploadHandler(w http.ResponseWriter, r *http.Request) { checkInternalServerError(err, w) defer rows.Close() - var count int + var count int64 for rows.Next() { if err := rows.Scan(&count); err != nil { log.Fatal(err) @@ -129,15 +131,12 @@ func uploadHandler(w http.ResponseWriter, r *http.Request) { defer file.Close() + // Regex to match the file extension reg, _ := regexp.Compile("\\.[0-9a-z]{1,5}$") - post.Extension = string(reg.Find([]byte(handler.Filename))) filename := fmt.Sprintf("%d%s", post.Id, post.Extension) - _, err = database.Exec("INSERT INTO postInfo(username, title, extension) VALUES(?, ?, ?);", - post.Username, post.Title, post.Extension) - f, err := os.OpenFile("./posts/"+filename, os.O_WRONLY|os.O_CREATE, 0666) checkInternalServerError(err, w) @@ -146,7 +145,8 @@ func uploadHandler(w http.ResponseWriter, r *http.Request) { _, err = io.Copy(f, file) checkInternalServerError(err, w) - fmt.Println(post) + _, err = database.Exec("INSERT INTO postInfo(username, title, extension) VALUES(?, ?, ?);", + post.Username, post.Title, post.Extension) http.Redirect(w, r, "/", http.StatusMovedPermanently) } @@ -179,17 +179,23 @@ func indexHandler(w http.ResponseWriter, _ *http.Request) { func postViewHandler(w http.ResponseWriter, r *http.Request) { - fmt.Println(r.RequestURI) + var post Post + + post.Id, _ = strconv.ParseInt(strings.Replace(r.URL.Path, "/h/", "", 1), 10, 32) + + err := database.QueryRow("SELECT title, username, extension FROM postInfo WHERE postId=?;", + post.Id).Scan(&post.Title, &post.Username, &post.Extension) + + checkInternalServerError(err, w) // TODO: Build this struct based on the information from the database hw := Homework{ - Id: 123, - Title: "[CS][370][Confer] First Homework", - PostImage: "image1.jpeg", + Title: post.Title, + PostImage: fmt.Sprintf("%d%s", post.Id, post.Extension), Comments: []string{"This post is great!", "No, it really isn't"}, } - err := tpl.ExecuteTemplate(w, "homework.gohtml", hw) + err = tpl.ExecuteTemplate(w, "homework.gohtml", hw) checkInternalServerError(err, w) } diff --git a/main.go b/main.go index f2a568a..ebbbf47 100644 --- a/main.go +++ b/main.go @@ -40,7 +40,8 @@ func main() { http.Handle("/static/", http.StripPrefix("/static", http.FileServer(http.Dir("./static")))) // Route for posts - http.HandleFunc("/h/*", postViewHandler) + http.HandleFunc("/h/", postViewHandler) + http.Handle("/h/img/", http.StripPrefix("/h/img", http.FileServer(http.Dir("./posts")))) http.HandleFunc("/login", loginHandler) http.HandleFunc("/logout", logoutHandler) diff --git a/models.go b/models.go index 8bcf552..5f6445b 100644 --- a/models.go +++ b/models.go @@ -1,7 +1,7 @@ package main type Homework struct { - Id uint + Id int64 Title string PostImage string Comments []string @@ -13,7 +13,7 @@ type User struct { } type Post struct { - Id int `json:"id"` + Id int64 `json:"id"` Username string `json:"username"` Title string `json:"title"` Extension string `json:"title"` diff --git a/templates/homework.gohtml b/templates/homework.gohtml index 5af63f0..56a90b1 100644 --- a/templates/homework.gohtml +++ b/templates/homework.gohtml @@ -5,7 +5,7 @@ {{template "top_bar"}}

-

PostImage:
+
PostImage:
Comments: {{range .Comments}} {{.}} {{end}}
diff --git a/templates/post_listing.gohtml b/templates/post_listing.gohtml index 3cea792..46029ec 100644 --- a/templates/post_listing.gohtml +++ b/templates/post_listing.gohtml @@ -5,7 +5,7 @@

{{.Title}}

+ src="/h/img/{{.PostImage}}">
diff --git a/utils.go b/utils.go index 8dcc7f4..a442c39 100644 --- a/utils.go +++ b/utils.go @@ -15,6 +15,6 @@ func checkInternalServerError(err error, w http.ResponseWriter) { func isAuthenticated(w http.ResponseWriter, r *http.Request) { if !authenticated { - http.Redirect(w, r, "/", http.StatusMovedPermanently) + http.Redirect(w, r, "/", http.StatusTemporaryRedirect) } } From b6b6fc83a8c31c0c4f127515079bf572d572fde6 Mon Sep 17 00:00:00 2001 From: Robby Zambito Date: Fri, 7 Dec 2018 02:11:25 -0500 Subject: [PATCH 45/62] Removed unneeded struct. --- handlers.go | 24 ++++++++++++++---------- models.go | 17 ++++++----------- 2 files changed, 20 insertions(+), 21 deletions(-) diff --git a/handlers.go b/handlers.go index e81853f..500bc26 100644 --- a/handlers.go +++ b/handlers.go @@ -102,7 +102,7 @@ func uploadHandler(w http.ResponseWriter, r *http.Request) { checkInternalServerError(err, w) - var post Post + var post Homework post.Username = user.Username @@ -154,13 +154,17 @@ func uploadHandler(w http.ResponseWriter, r *http.Request) { func indexHandler(w http.ResponseWriter, _ *http.Request) { // TODO: Query the database to populate this array. - posts := []Homework{ - { - Id: 123, - Title: "[CS][370][Confer] First Homework", - PostImage: "image1.jpeg", - Comments: []string{"This post is great!", "No, it really isn't"}, - }, + + var posts []Homework + + rows, err := database.Query("SELECT * FROM postInfo") + + for rows.Next() { + var curPost Homework + if err := rows.Scan(&curPost.Id, &curPost.Username, &curPost.Title, &curPost.Extension); err != nil { + log.Fatal(err) + } + posts = append(posts, curPost) } indexData := struct { @@ -171,7 +175,7 @@ func indexHandler(w http.ResponseWriter, _ *http.Request) { posts, } - err := tpl.ExecuteTemplate(w, "index.gohtml", indexData) + err = tpl.ExecuteTemplate(w, "index.gohtml", indexData) checkInternalServerError(err, w) @@ -179,7 +183,7 @@ func indexHandler(w http.ResponseWriter, _ *http.Request) { func postViewHandler(w http.ResponseWriter, r *http.Request) { - var post Post + var post Homework post.Id, _ = strconv.ParseInt(strings.Replace(r.URL.Path, "/h/", "", 1), 10, 32) diff --git a/models.go b/models.go index 5f6445b..986c64c 100644 --- a/models.go +++ b/models.go @@ -1,20 +1,15 @@ package main type Homework struct { - Id int64 - Title string - PostImage string - Comments []string + Id int64 `json:"id"` + Title string `json:"title"` + Username string `json:"username"` + PostImage string `json:"postImage"` + Extension string `json:"extension"` + Comments []string `json:"comments"` } type User struct { Username string `json:"username"` Password string `json:"password"` } - -type Post struct { - Id int64 `json:"id"` - Username string `json:"username"` - Title string `json:"title"` - Extension string `json:"title"` -} From 3453b484d4017db6828f044bfe369636f5f4747a Mon Sep 17 00:00:00 2001 From: Robby Zambito Date: Fri, 7 Dec 2018 02:14:32 -0500 Subject: [PATCH 46/62] Can view new posts on index page. --- handlers.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/handlers.go b/handlers.go index 500bc26..0377173 100644 --- a/handlers.go +++ b/handlers.go @@ -164,6 +164,8 @@ func indexHandler(w http.ResponseWriter, _ *http.Request) { if err := rows.Scan(&curPost.Id, &curPost.Username, &curPost.Title, &curPost.Extension); err != nil { log.Fatal(err) } + + curPost.PostImage = fmt.Sprintf("%d%s", curPost.Id, curPost.Extension) posts = append(posts, curPost) } From c57055ee25e14f9250c80fbc3e4cb53cb5729095 Mon Sep 17 00:00:00 2001 From: Robby Zambito Date: Fri, 7 Dec 2018 02:16:59 -0500 Subject: [PATCH 47/62] Added username to posts on index page. --- handlers.go | 4 ---- templates/post_listing.gohtml | 1 + 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/handlers.go b/handlers.go index 0377173..7aaccad 100644 --- a/handlers.go +++ b/handlers.go @@ -152,9 +152,6 @@ func uploadHandler(w http.ResponseWriter, r *http.Request) { } func indexHandler(w http.ResponseWriter, _ *http.Request) { - - // TODO: Query the database to populate this array. - var posts []Homework rows, err := database.Query("SELECT * FROM postInfo") @@ -194,7 +191,6 @@ func postViewHandler(w http.ResponseWriter, r *http.Request) { checkInternalServerError(err, w) - // TODO: Build this struct based on the information from the database hw := Homework{ Title: post.Title, PostImage: fmt.Sprintf("%d%s", post.Id, post.Extension), diff --git a/templates/post_listing.gohtml b/templates/post_listing.gohtml index 46029ec..e6db175 100644 --- a/templates/post_listing.gohtml +++ b/templates/post_listing.gohtml @@ -8,6 +8,7 @@ src="/h/img/{{.PostImage}}">
+ {{.Username}}
{{end}} From 1a7c67e6ce1eca041b4bf5f72ec8b89e3410c64d Mon Sep 17 00:00:00 2001 From: Robby Zambito Date: Fri, 7 Dec 2018 02:18:52 -0500 Subject: [PATCH 48/62] Removed unneeded sidebar --- templates/index.gohtml | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/templates/index.gohtml b/templates/index.gohtml index 0cd7c8f..6929674 100644 --- a/templates/index.gohtml +++ b/templates/index.gohtml @@ -7,27 +7,6 @@ {{range .Posts}} {{template "post_listing" .}} {{end}}
- -
- [CS] -
- [370] - [Confer] -
- -
- [240] - [Sarner] -
-
- -
- [MAT] -
- [413] - [Andriamanalimanana] -
-
From ec04ea8e3bb91df6e31406ab6924dd651f7278df Mon Sep 17 00:00:00 2001 From: Robby Zambito Date: Fri, 7 Dec 2018 02:38:34 -0500 Subject: [PATCH 49/62] Can add comments and view them per each post. --- handlers.go | 27 +++++++++++++++++++++++++-- main.go | 1 + templates/homework.gohtml | 10 ++++++++-- 3 files changed, 34 insertions(+), 4 deletions(-) diff --git a/handlers.go b/handlers.go index 7aaccad..e97a6b1 100644 --- a/handlers.go +++ b/handlers.go @@ -181,7 +181,6 @@ func indexHandler(w http.ResponseWriter, _ *http.Request) { } func postViewHandler(w http.ResponseWriter, r *http.Request) { - var post Homework post.Id, _ = strconv.ParseInt(strings.Replace(r.URL.Path, "/h/", "", 1), 10, 32) @@ -191,13 +190,37 @@ func postViewHandler(w http.ResponseWriter, r *http.Request) { checkInternalServerError(err, w) + var comments []string + + rows, err := database.Query("SELECT comment FROM commentSection WHERE postId=?;", post.Id) + + for rows.Next() { + var curComment string + if err := rows.Scan(&curComment); err != nil { + log.Fatal(err) + } + + comments = append(comments, curComment) + } + hw := Homework{ + Id: post.Id, Title: post.Title, PostImage: fmt.Sprintf("%d%s", post.Id, post.Extension), - Comments: []string{"This post is great!", "No, it really isn't"}, + Comments: comments, } err = tpl.ExecuteTemplate(w, "homework.gohtml", hw) checkInternalServerError(err, w) } + +func commentHandler(w http.ResponseWriter, r *http.Request) { + + id, _ := strconv.ParseInt(strings.Replace(r.URL.Path, "/comment/", "", 1), 10, 32) + + _, err = database.Exec("INSERT INTO commentSection(postId, comment) VALUES(?, ?);", id, r.FormValue("comment")) + + http.Redirect(w, r, fmt.Sprintf("/h/%d", id), http.StatusMovedPermanently) + +} diff --git a/main.go b/main.go index ebbbf47..abb2a54 100644 --- a/main.go +++ b/main.go @@ -47,6 +47,7 @@ func main() { http.HandleFunc("/logout", logoutHandler) http.HandleFunc("/register", registerHandler) http.HandleFunc("/upload", uploadHandler) + http.HandleFunc("/comment/", commentHandler) port := ":3000" diff --git a/templates/homework.gohtml b/templates/homework.gohtml index 56a90b1..2e90a9b 100644 --- a/templates/homework.gohtml +++ b/templates/homework.gohtml @@ -5,14 +5,20 @@ {{template "top_bar"}}

-

PostImage:
+
PostImage:
-
Comments: {{range .Comments}} {{.}} {{end}}
+
Comments:
{{range .Comments}} {{.}}
{{end}}

+
+ + + +
+ {{template "footer"}} \ No newline at end of file From fbb2ab40fcfcad115ac13eeb1a72a0fc8f5a2a1b Mon Sep 17 00:00:00 2001 From: Robby Zambito Date: Fri, 7 Dec 2018 03:17:29 -0500 Subject: [PATCH 50/62] Removed unused hardcoded image previously used to show as an example post. --- static/img/image1.jpeg | Bin 151580 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 static/img/image1.jpeg diff --git a/static/img/image1.jpeg b/static/img/image1.jpeg deleted file mode 100644 index c6282bc0057c9ed865aaa8b72853cb54dd6e3993..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 151580 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ξ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�>I^-}lq6dy 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

O%{JK($bXs0IBO*i^Oe)BASV})?_)W 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 V>HPIgJfnDJGOsKfj`HV${{j6V Date: Sun, 9 Dec 2018 16:19:25 -0500 Subject: [PATCH 51/62] Removed unneeded init scripts. --- init/databaseCreate.go | 32 -------------------------------- init/init.sh | 33 --------------------------------- init/start_db.sh | 4 ---- init/uninit.sh | 7 ------- 4 files changed, 76 deletions(-) delete mode 100644 init/databaseCreate.go delete mode 100644 init/init.sh delete mode 100644 init/start_db.sh delete mode 100644 init/uninit.sh diff --git a/init/databaseCreate.go b/init/databaseCreate.go deleted file mode 100644 index 6fe5424..0000000 --- a/init/databaseCreate.go +++ /dev/null @@ -1,32 +0,0 @@ -package main - -import ( - "database/sql" - //"fmt" - _ "github.com/go-sql-driver/mysql" -) - -func main() { - create("homeworkHub") -} - -func create(name string) { - db, err := sql.Open("mysql", "root:password@/") - if err != nil { - panic(err) - } - defer db.Close() - - _, err = db.Exec("CREATE DATABASE " + name) - if err != nil { - panic(err) - } - _, err = db.Exec("USE " + name) - if err != nil { - panic(err) - } - _, err = db.Exec("CREATE TABLE userInfo(user_id integer NOT NULL AUTO_INCREMENT PRIMARY KEY, email varchar(32) NOT NULL, username varchar(32) NOT NULL, isAdmin varchar(6), passwordHash varchar(32) NOT NULL )") - if err != nil { - panic(err) - } -} diff --git a/init/init.sh b/init/init.sh deleted file mode 100644 index 55ebdef..0000000 --- a/init/init.sh +++ /dev/null @@ -1,33 +0,0 @@ -#!/bin/sh - - -is_installed() { - search="$(apt-cache pkgnames | grep -i $1)" - - if [ -z "${search}" ]; then - return 1 - fi - - return 0 -} - - -# Install mysql-server -get_mysql() { - p="mysql-server" - - if [ ! $(is_installed $p) ]; then - # is not installed - echo "$p is not installed" - - echo "Installing... " - sudo apt update - sudo apt install mysql-server -y - - fi - - echo "Installed: " - apt-cache pkgnames | grep -i $p -} - -get_mysql diff --git a/init/start_db.sh b/init/start_db.sh deleted file mode 100644 index f55f9a8..0000000 --- a/init/start_db.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/sh - -systemctl start mysql - diff --git a/init/uninit.sh b/init/uninit.sh deleted file mode 100644 index 55fc0df..0000000 --- a/init/uninit.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/sh - -systemctl stop mysql - -sudo apt remove mysql-server - -sudo apt autoremove From 3302bf4ed4ebedd01448ea1e5e66b92a5fce67b2 Mon Sep 17 00:00:00 2001 From: Robby Zambito Date: Sun, 9 Dec 2018 16:26:53 -0500 Subject: [PATCH 52/62] Fixed top bar on post page. --- handlers.go | 10 +++++++++- templates/homework.gohtml | 8 ++++---- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/handlers.go b/handlers.go index e97a6b1..fbef67d 100644 --- a/handlers.go +++ b/handlers.go @@ -210,7 +210,15 @@ func postViewHandler(w http.ResponseWriter, r *http.Request) { Comments: comments, } - err = tpl.ExecuteTemplate(w, "homework.gohtml", hw) + postViewData := struct { + Authenticated bool + Hw Homework + }{ + authenticated, + hw, + } + + err = tpl.ExecuteTemplate(w, "homework.gohtml", postViewData) checkInternalServerError(err, w) } diff --git a/templates/homework.gohtml b/templates/homework.gohtml index 2e90a9b..8c47896 100644 --- a/templates/homework.gohtml +++ b/templates/homework.gohtml @@ -2,18 +2,18 @@

{{end}} \ No newline at end of file From 2d56c4c01454e672d893e7cfbcc0b4bd7bb3e44a Mon Sep 17 00:00:00 2001 From: Robby Zambito Date: Sun, 9 Dec 2018 16:41:19 -0500 Subject: [PATCH 54/62] Only prompt to add a comment when the user is logged in --- templates/homework.gohtml | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/templates/homework.gohtml b/templates/homework.gohtml index 8c47896..9f7bb86 100644 --- a/templates/homework.gohtml +++ b/templates/homework.gohtml @@ -4,20 +4,19 @@ {{template "top_bar" .}} -

-

PostImage:
+
PostImage:
-
Comments:
{{range .Hw.Comments}} {{.}}
{{end}}
+
Comments:
{{range .Hw.Comments}} {{.}}
{{end}}
-

- - - - - - + {{if .Authenticated}} +
+ + + +
+ {{end}} From 52266e7d0db9f023700027f66a1f71c06d76df40 Mon Sep 17 00:00:00 2001 From: Robby Zambito Date: Sun, 9 Dec 2018 18:48:06 -0500 Subject: [PATCH 55/62] Updated individual post page. --- templates/homework.gohtml | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/templates/homework.gohtml b/templates/homework.gohtml index 9f7bb86..9ee6dbb 100644 --- a/templates/homework.gohtml +++ b/templates/homework.gohtml @@ -5,10 +5,24 @@ {{template "top_bar" .}} -
PostImage:
- - -
Comments:
{{range .Hw.Comments}} {{.}}
{{end}}
+
+

{{.Hw.Title}}

+ +
+ + +
+ {{ if eq (len .Hw.Comments) 0 }} +

There are no comments on this post!

+ {{else}} +
Comments:
+ {{range $comment := .Hw.Comments}} +

{{$comment}}

+ {{end}} + {{end}} + +
{{if .Authenticated}}
From 8f663ace8be62e62306a529772c6849c66c8900b Mon Sep 17 00:00:00 2001 From: Robby Zambito Date: Sun, 9 Dec 2018 18:53:01 -0500 Subject: [PATCH 56/62] Reformatted code. Added class to post container element. --- templates/homework.gohtml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/templates/homework.gohtml b/templates/homework.gohtml index 9ee6dbb..99c6dd0 100644 --- a/templates/homework.gohtml +++ b/templates/homework.gohtml @@ -5,7 +5,7 @@ {{template "top_bar" .}} -
+

{{.Hw.Title}}

@@ -13,7 +13,7 @@
- {{ if eq (len .Hw.Comments) 0 }} + {{if eq (len .Hw.Comments) 0}}

There are no comments on this post!

{{else}}
Comments:
@@ -26,8 +26,9 @@ {{if .Authenticated}} - - + {{end}} From 2b286480925390d0702a367a7ce40f557f81f862 Mon Sep 17 00:00:00 2001 From: Robby Zambito Date: Sun, 9 Dec 2018 19:00:59 -0500 Subject: [PATCH 57/62] Made the styling from the index page to the individual post page. --- templates/homework.gohtml | 3 ++- templates/post_listing.gohtml | 6 +++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/templates/homework.gohtml b/templates/homework.gohtml index 99c6dd0..dbac78b 100644 --- a/templates/homework.gohtml +++ b/templates/homework.gohtml @@ -7,8 +7,9 @@

{{.Hw.Title}}

+ {{.Hw.Username}} + class="img-fluid mx-auto d-block">
diff --git a/templates/post_listing.gohtml b/templates/post_listing.gohtml index e6db175..0fbcfd4 100644 --- a/templates/post_listing.gohtml +++ b/templates/post_listing.gohtml @@ -3,12 +3,16 @@

{{.Title}}

+
+ + {{.Username}} + +
- {{.Username}}
{{end}} From 1dacdf258785a0d196c66bca79044aa45c006f05 Mon Sep 17 00:00:00 2001 From: Robby Zambito Date: Mon, 10 Dec 2018 15:38:31 -0500 Subject: [PATCH 58/62] Added username on post page --- handlers.go | 1 + 1 file changed, 1 insertion(+) diff --git a/handlers.go b/handlers.go index fbef67d..a0a358b 100644 --- a/handlers.go +++ b/handlers.go @@ -207,6 +207,7 @@ func postViewHandler(w http.ResponseWriter, r *http.Request) { Id: post.Id, Title: post.Title, PostImage: fmt.Sprintf("%d%s", post.Id, post.Extension), + Username: post.Username, Comments: comments, } From 6cfd662c715f0d8cf5d0a64ccc2959673116a359 Mon Sep 17 00:00:00 2001 From: Robby Zambito Date: Mon, 10 Dec 2018 15:55:17 -0500 Subject: [PATCH 59/62] Added alert when login fails --- handlers.go | 28 ++++++++++++++++++---------- templates/login.gohtml | 8 ++++++++ 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/handlers.go b/handlers.go index a0a358b..f4139a2 100644 --- a/handlers.go +++ b/handlers.go @@ -55,9 +55,18 @@ func registerHandler(w http.ResponseWriter, r *http.Request) { func loginHandler(w http.ResponseWriter, r *http.Request) { + type loginPageDataStruct = struct { + Failed bool + Authenticated bool + } + if r.Method != "POST" { - err := tpl.ExecuteTemplate(w, "login.gohtml", nil) - checkInternalServerError(err, w) + if !authenticated { + _ = tpl.ExecuteTemplate(w, "login.gohtml", loginPageDataStruct{false, false}) + + } else { + http.Redirect(w, r, "/", http.StatusMovedPermanently) + } return } @@ -67,19 +76,18 @@ func loginHandler(w http.ResponseWriter, r *http.Request) { password := r.FormValue("password") // query database to get match username - err := database.QueryRow("SELECT username, password FROM userInfo WHERE username=?;", + _ = database.QueryRow("SELECT username, password FROM userInfo WHERE username=?;", username).Scan(&user.Username, &user.Password) - checkInternalServerError(err, w) - // validate password err = bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(password)) - checkInternalServerError(err, w) - - authenticated = true - - http.Redirect(w, r, "/", http.StatusMovedPermanently) + if err == nil { + authenticated = true + http.Redirect(w, r, "/", http.StatusTemporaryRedirect) + } else { + _ = tpl.ExecuteTemplate(w, "login.gohtml", loginPageDataStruct{true, false}) + } } diff --git a/templates/login.gohtml b/templates/login.gohtml index 14d92de..b402efb 100644 --- a/templates/login.gohtml +++ b/templates/login.gohtml @@ -1,10 +1,18 @@ {{template "header"}} +{{template "top_bar" .}} +
Username: Password:
+{{if .Failed}} + +{{end}} + {{template "footer"}} \ No newline at end of file From 648db11205f3db65acc2644cccdd3f6fd8b6c792 Mon Sep 17 00:00:00 2001 From: Robby Zambito Date: Mon, 10 Dec 2018 15:58:43 -0500 Subject: [PATCH 60/62] Added container to login page along with top bar. --- templates/login.gohtml | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/templates/login.gohtml b/templates/login.gohtml index b402efb..593a4cd 100644 --- a/templates/login.gohtml +++ b/templates/login.gohtml @@ -1,18 +1,22 @@ {{template "header"}} -{{template "top_bar" .}} +
+ {{template "top_bar" .}} -
- Username: - Password: - -
+
+

Username:

-{{if .Failed}} - -{{end}} +

Password:

+ + +
+ + {{if .Failed}} + + {{end}} +
{{template "footer"}} \ No newline at end of file From 8513b31ae0b833b30938cfc2cb86aed3eee5f58a Mon Sep 17 00:00:00 2001 From: Robby Zambito Date: Mon, 10 Dec 2018 16:16:04 -0500 Subject: [PATCH 61/62] Added specific errors for registering an account. Made the styling consistent across all pages now. --- handlers.go | 23 +++++++++------- templates/register.gohtml | 55 ++++++++++++++++++++++++++------------- 2 files changed, 51 insertions(+), 27 deletions(-) diff --git a/handlers.go b/handlers.go index f4139a2..5d4de39 100644 --- a/handlers.go +++ b/handlers.go @@ -15,10 +15,19 @@ import ( func registerHandler(w http.ResponseWriter, r *http.Request) { + type registerPageDataStruct = struct { + UsernameTaken bool + PasswordMismatch bool + Authenticated bool + } + if r.Method != "POST" { - err := tpl.ExecuteTemplate(w, "register.gohtml", nil) - checkInternalServerError(err, w) + if !authenticated { + _ = tpl.ExecuteTemplate(w, "register.gohtml", registerPageDataStruct{false, false, false}) + } else { + http.Redirect(w, r, "/", http.StatusMovedPermanently) + } return } @@ -26,8 +35,7 @@ func registerHandler(w http.ResponseWriter, r *http.Request) { // grab user info username := r.FormValue("username") password := r.FormValue("password") - - fmt.Printf("Name entered: %s\tPass entered: %s\n", username, password) + passAgain := r.FormValue("pass-again") // Check existence of user var user User @@ -36,7 +44,7 @@ func registerHandler(w http.ResponseWriter, r *http.Request) { switch { // user is available - case err == sql.ErrNoRows: + case err == sql.ErrNoRows && password == passAgain: hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) checkInternalServerError(err, w) // insert to database @@ -45,11 +53,8 @@ func registerHandler(w http.ResponseWriter, r *http.Request) { checkInternalServerError(err, w) http.Redirect(w, r, "/login", http.StatusMovedPermanently) - case err != nil: - http.Error(w, "loi: "+err.Error(), http.StatusBadRequest) - return default: - http.Redirect(w, r, "/login", http.StatusMovedPermanently) + _ = tpl.ExecuteTemplate(w, "register.gohtml", registerPageDataStruct{err != sql.ErrNoRows, password != passAgain, false}) } } diff --git a/templates/register.gohtml b/templates/register.gohtml index a8a9b55..a123355 100644 --- a/templates/register.gohtml +++ b/templates/register.gohtml @@ -1,27 +1,46 @@ {{template "header"}} -
-

Register

-
-
- - -
+
+ + {{template "top_bar" .}} + + + +

+ + +

- - -
- - -
+

+ + +

- -
+

+ + +

- Login -
- + +
+ Login +
+ + + {{if .UsernameTaken}} + + {{end}} + + {{if .PasswordMismatch}} + + {{end}} + +
{{template "footer"}} \ No newline at end of file From 19984a680a07edcf7756c0906ea9f94ba34ebc28 Mon Sep 17 00:00:00 2001 From: Robby Zambito Date: Mon, 10 Dec 2018 16:36:08 -0500 Subject: [PATCH 62/62] Update README.md --- README.md | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 13c15dd..af78d13 100644 --- a/README.md +++ b/README.md @@ -7,20 +7,31 @@ studying ability* ## Installing +To run this project, `golang-go` and `git` must be installed on your system. They can both be downloaded using the +command: +```bash +sudo apt install golang-go git -y +``` + Download the source using the command: ```bash git clone https://github.com/SocialHW/HomeworkHub.git ``` -Running this project no longer requires any external dependencies to be running, such as MySQL. +Running this project no longer requires any external dependencies to be running, such as MySQL. However, you must +install the third party libraries we are using for our cryptographic functions, and for interacting with our +SQLite3 database. To do so, run the command: +```bash +go get golang.org/x/crypto/bcrypt github.com/mattn/go-sqlite3 +``` ## Building and Running To run the project as a Go script, simply run this command from the root directory of the project: ```bash -go run main.go +go run *.go ``` @@ -44,4 +55,4 @@ git flow feature start This will create a new branch named `feature/`, and checkout that branch so you can immediately start work on it. When the feature is finished and you would like to merge, make sure you have commited and pushed all of your changes, and head to GitHub where you can make a new pull request for that feature. Make sure to merge into the develop -branch. \ No newline at end of file +branch.