diff --git a/.gitignore b/.gitignore index 10efe95..7efc66b 100644 --- a/.gitignore +++ b/.gitignore @@ -10,9 +10,15 @@ *.dll *.so *.dylib +*.db +*.sqlite3 # Test binary, build with `go test -c` *.test # Output of the go coverage tool, specifically when used with LiteIDE *.out + + +*.old + diff --git a/README.md b/README.md index 0d4da1c..af78d13 100644 --- a/README.md +++ b/README.md @@ -7,32 +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 ``` -Installing the required dependencies to run (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 -cd HomeworkHub/ -sh init/init.sh +go get golang.org/x/crypto/bcrypt github.com/mattn/go-sqlite3 ``` ## 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 -go run main.go +go run *.go ``` @@ -56,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. diff --git a/db/comment_section.go b/db/comment_section.go new file mode 100644 index 0000000..7bbfec2 --- /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", "./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/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/post_info.go b/db/post_info.go new file mode 100644 index 0000000..5958d50 --- /dev/null +++ b/db/post_info.go @@ -0,0 +1,13 @@ +package main + +import ( + "database/sql" + + _ "github.com/mattn/go-sqlite3" +) + +func main() { + 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/userTest.go b/db/userTest.go deleted file mode 100644 index 232603f..0000000 --- a/db/userTest.go +++ /dev/null @@ -1,48 +0,0 @@ -package main - -import ( - "bufio" - "database/sql" - "fmt" - _ "github.com/go-sql-driver/mysql" - "os" -) - -func main() { - userMake("homeworkHub") -} - -func userMake(name string) { - - reader := bufio.NewReader(os.Stdin) - // var username string - fmt.Println("Enter Test Username: ") - username, _ := reader.ReadString('\n') - - // var email string - fmt.Println("Enter Test Email: ") - email, _ := reader.ReadString('\n') - - // var isadmin string - fmt.Println("Is User Admin?") - isadmin, _ := reader.ReadString('\n') - - // var passwordhash string - fmt.Println("Enter Password Hash: ") - passwordhash, _ := reader.ReadString('\n') - - 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,isAdmin,passwordHash) VALUES('%s', '%s', '%s', '%s');", email, username, isadmin, passwordhash)) - if err != nil { - panic(err) - } -} 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) - } -} diff --git a/db/user_info.go b/db/user_info.go new file mode 100644 index 0000000..6a53bb6 --- /dev/null +++ b/db/user_info.go @@ -0,0 +1,13 @@ +package main + +import ( + "database/sql" + + _ "github.com/mattn/go-sqlite3" +) + +func main() { + 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/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..b8a4aba --- /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 | extension 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 diff --git a/handlers.go b/handlers.go new file mode 100644 index 0000000..5d4de39 --- /dev/null +++ b/handlers.go @@ -0,0 +1,248 @@ +package main + +import ( + "database/sql" + "fmt" + "golang.org/x/crypto/bcrypt" + "io" + "log" + "net/http" + "os" + "regexp" + "strconv" + "strings" +) + +func registerHandler(w http.ResponseWriter, r *http.Request) { + + type registerPageDataStruct = struct { + UsernameTaken bool + PasswordMismatch bool + Authenticated bool + } + + if r.Method != "POST" { + + if !authenticated { + _ = tpl.ExecuteTemplate(w, "register.gohtml", registerPageDataStruct{false, false, false}) + } else { + http.Redirect(w, r, "/", http.StatusMovedPermanently) + } + + return + } + + // grab user info + username := r.FormValue("username") + password := r.FormValue("password") + passAgain := r.FormValue("pass-again") + + // Check existence of user + var user User + err := database.QueryRow("SELECT username, password FROM userInfo WHERE username=?;", + username).Scan(&user.Username, &user.Password) + + switch { + // user is available + case err == sql.ErrNoRows && password == passAgain: + hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) + checkInternalServerError(err, w) + // insert to database + + _, err = database.Exec("INSERT INTO userInfo(username, password) VALUES(?, ?);", username, hashedPassword) + + checkInternalServerError(err, w) + http.Redirect(w, r, "/login", http.StatusMovedPermanently) + default: + _ = tpl.ExecuteTemplate(w, "register.gohtml", registerPageDataStruct{err != sql.ErrNoRows, password != passAgain, false}) + } +} + +func loginHandler(w http.ResponseWriter, r *http.Request) { + + type loginPageDataStruct = struct { + Failed bool + Authenticated bool + } + + if r.Method != "POST" { + if !authenticated { + _ = tpl.ExecuteTemplate(w, "login.gohtml", loginPageDataStruct{false, false}) + + } else { + http.Redirect(w, r, "/", http.StatusMovedPermanently) + } + + return + } + + // grab user info from the submitted form + username := r.FormValue("username") + password := r.FormValue("password") + + // query database to get match username + _ = database.QueryRow("SELECT username, password FROM userInfo WHERE username=?;", + username).Scan(&user.Username, &user.Password) + + // validate password + err = bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(password)) + + if err == nil { + authenticated = true + http.Redirect(w, r, "/", http.StatusTemporaryRedirect) + } else { + _ = tpl.ExecuteTemplate(w, "login.gohtml", loginPageDataStruct{true, false}) + } + +} + +func logoutHandler(w http.ResponseWriter, r *http.Request) { + authenticated = false + isAuthenticated(w, r) +} + +func uploadHandler(w http.ResponseWriter, r *http.Request) { + isAuthenticated(w, r) + + if r.Method == "GET" { + err := tpl.ExecuteTemplate(w, "upload.gohtml", nil) + checkInternalServerError(err, w) + + return + } + + err = r.ParseMultipartForm(32 << 20) + + checkInternalServerError(err, w) + + var post Homework + + post.Username = user.Username + + file, handler, err := r.FormFile("upload-file") + + checkInternalServerError(err, w) + + rows, err := database.Query("SELECT COUNT(*) FROM postInfo") + + checkInternalServerError(err, w) + defer rows.Close() + var count int64 + 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() + + // 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) + + f, err := os.OpenFile("./posts/"+filename, os.O_WRONLY|os.O_CREATE, 0666) + + checkInternalServerError(err, w) + + defer f.Close() + _, err = io.Copy(f, file) + checkInternalServerError(err, w) + + _, err = database.Exec("INSERT INTO postInfo(username, title, extension) VALUES(?, ?, ?);", + post.Username, post.Title, post.Extension) + + http.Redirect(w, r, "/", http.StatusMovedPermanently) +} + +func indexHandler(w http.ResponseWriter, _ *http.Request) { + 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) + } + + curPost.PostImage = fmt.Sprintf("%d%s", curPost.Id, curPost.Extension) + posts = append(posts, curPost) + } + + indexData := struct { + Authenticated bool + Posts []Homework + }{ + authenticated, + posts, + } + + err = tpl.ExecuteTemplate(w, "index.gohtml", indexData) + + checkInternalServerError(err, w) + +} + +func postViewHandler(w http.ResponseWriter, r *http.Request) { + var post Homework + + 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) + + 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), + Username: post.Username, + Comments: comments, + } + + postViewData := struct { + Authenticated bool + Hw Homework + }{ + authenticated, + hw, + } + + err = tpl.ExecuteTemplate(w, "homework.gohtml", postViewData) + + 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/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 diff --git a/main.go b/main.go index 6778251..abb2a54 100644 --- a/main.go +++ b/main.go @@ -5,78 +5,84 @@ package main import ( + "database/sql" + _ "github.com/mattn/go-sqlite3" "html/template" "log" "net/http" ) -var tpl *template.Template +var ( + tpl *template.Template + authenticated = false + database *sql.DB + err error + user User +) func init() { tpl = template.Must(template.ParseGlob("templates/*.gohtml")) } 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{ - { - 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"}, - }, - }, - }) - + initializeDb() + defer func() { + err = database.Close() if err != nil { - log.Println(err) - http.Error(w, "Internal server error", http.StatusInternalServerError) - return + panic(err) } - }) + }() + + // 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/", func(w http.ResponseWriter, req *http.Request) { - - hw := homework{ - 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"}, - } + // Route for posts + http.HandleFunc("/h/", postViewHandler) + http.Handle("/h/img/", http.StripPrefix("/h/img", http.FileServer(http.Dir("./posts")))) - 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("/upload", uploadHandler) + http.HandleFunc("/comment/", commentHandler) 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) + } } -type homework struct { - Id uint - Title string - PostImage string - Upvotes uint - Downvotes uint - Comments []string - Tags []string +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)") + _, err = statement.Exec() + if err != nil { + panic(err) + } + + 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 commentSection (postId INTEGER, username TEXT, comment TEXT)") + _, err = statement.Exec() + if err != nil { + panic(err) + } + + err = database.Ping() + + if err != nil { + panic(err) + } + } diff --git a/models.go b/models.go new file mode 100644 index 0000000..986c64c --- /dev/null +++ b/models.go @@ -0,0 +1,15 @@ +package main + +type Homework struct { + 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"` +} 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/static/css/style.css b/static/css/style.css index 206cf74..29876cc 100644 --- a/static/css/style.css +++ b/static/css/style.css @@ -1,32 +1,36 @@ 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; +} + +#upload-button { + margin-right: 3em; } \ No newline at end of file diff --git a/static/img/image1.jpeg b/static/img/image1.jpeg deleted file mode 100644 index c6282bc..0000000 Binary files a/static/img/image1.jpeg and /dev/null differ diff --git a/templates/homework.gohtml b/templates/homework.gohtml index 597dc00..dbac78b 100644 --- a/templates/homework.gohtml +++ b/templates/homework.gohtml @@ -1,38 +1,39 @@ {{template "header"}} -
+
- {{template "top_bar"}} + {{template "top_bar" .}} -

-

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

+ {{if .Authenticated}} +
+ + +
+ {{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 diff --git a/templates/index.gohtml b/templates/index.gohtml index 5258922..6929674 100644 --- a/templates/index.gohtml +++ b/templates/index.gohtml @@ -1,33 +1,12 @@ {{template "header"}}
- {{template "top_bar"}} + {{template "top_bar" .}}
{{range .Posts}} {{template "post_listing" .}} {{end}}
- -
- [CS] -
- [370] - [Confer] -
- -
- [240] - [Sarner] -
-
- -
- [MAT] -
- [413] - [Andriamanalimanana] -
-
diff --git a/templates/login.gohtml b/templates/login.gohtml new file mode 100644 index 0000000..593a4cd --- /dev/null +++ b/templates/login.gohtml @@ -0,0 +1,22 @@ +{{template "header"}} + +
+ {{template "top_bar" .}} + +
+

Username:

+ +

Password:

+ + +
+ + {{if .Failed}} + + {{end}} +
+ + +{{template "footer"}} \ No newline at end of file diff --git a/templates/post_listing.gohtml b/templates/post_listing.gohtml index 3cea792..0fbcfd4 100644 --- a/templates/post_listing.gohtml +++ b/templates/post_listing.gohtml @@ -3,9 +3,14 @@

{{.Title}}

+
+ + {{.Username}} + +
+ src="/h/img/{{.PostImage}}">
diff --git a/templates/register.gohtml b/templates/register.gohtml new file mode 100644 index 0000000..a123355 --- /dev/null +++ b/templates/register.gohtml @@ -0,0 +1,46 @@ +{{template "header"}} + +
+ + {{template "top_bar" .}} + +
+ +

+ + +

+ + +

+ + +

+ +

+ + +

+ + + +
+ Login +
+
+ + {{if .UsernameTaken}} + + {{end}} + + {{if .PasswordMismatch}} + + {{end}} + +
+ +{{template "footer"}} \ No newline at end of file diff --git a/templates/top_bar.gohtml b/templates/top_bar.gohtml index bf042be..a0c8339 100644 --- a/templates/top_bar.gohtml +++ b/templates/top_bar.gohtml @@ -2,6 +2,15 @@

Social HW

- Login or Register + {{if not .Authenticated}} + + Login or Register + + {{else}} + + + Logout + + {{end}}
{{end}} \ No newline at end of file diff --git a/templates/upload.gohtml b/templates/upload.gohtml new file mode 100644 index 0000000..6a36f6c --- /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 new file mode 100644 index 0000000..a442c39 --- /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, "/", http.StatusTemporaryRedirect) + } +}