{{.CurrentPost.Title}}
-{{.CurrentPost.Username}}
-{{.CurrentPost.Content}}
- -- Created: {{.CurrentPost.Created.Format "15:04 January 2, 2006"}} -
-diff --git a/cmd/web/main.go b/cmd/web/main.go index a997a4a..d6ffe49 100644 --- a/cmd/web/main.go +++ b/cmd/web/main.go @@ -49,6 +49,10 @@ func main() { app.GitHubClientSecret = "2327f7f8677894e2726f70696d9321399efb6b61" app.GitHubRedirectURL = "http://localhost:8080/github-callback" + app.GoogleClientID = "1045980296590-9c2dpdmdbp17kiih3oev5nsk55a33q29.apps.googleusercontent.com" + app.GoogleClientSecret = "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" + app.GoogleRedirectURL = "http://localhost:8080/google-callback" + //the list of games that are represented and will be covered on site. app.GamesList = map[string]string{ "Lineage 2": "L2", diff --git a/cmd/web/routes.go b/cmd/web/routes.go index 05dd299..3bd19ac 100644 --- a/cmd/web/routes.go +++ b/cmd/web/routes.go @@ -43,6 +43,9 @@ func routes(a *config.AppConfig) http.Handler { mux.HandleFunc("/login-github", handler.Repo.LoginWithGitHubHandler) mux.HandleFunc("/github-callback", handler.Repo.CallbackGitHubHandler) + mux.HandleFunc("/login-google", handler.Repo.LoginWithGoogleHandler) + mux.HandleFunc("/google-callback", handler.Repo.CallbackGoogleHandler) + return mux } diff --git a/example/README.md b/example/README.md deleted file mode 100644 index 4d30807..0000000 --- a/example/README.md +++ /dev/null @@ -1,29 +0,0 @@ -# Go Forum Authenticate Web Application - -Go Forum Authentication is a vanilla Forum application with additional authentication methods. - - -### Objectives - -The goal of this project is to implement, into your forum, new ways of authentication. You have to be able to register and to login using at least Google and Github authentication tools. - -Some examples of authentication means are: - -- Facebook -- GitHub -- Google - -### Instructions - -- To login with Google give contributors you Google e-mail to put your name on google whitelist. -- Your project must have implemented at least the two authentication examples given. -- Your project must be written in **Go**. -- The code must respect the [**good practices**](../../good-practices/README.md). - -## Contributing - -- Kveber -- Karl-Thomas - - - diff --git a/example/config/db.go b/example/config/db.go deleted file mode 100644 index bc7ee78..0000000 --- a/example/config/db.go +++ /dev/null @@ -1,15 +0,0 @@ -package config - -import ( - "database/sql" - - _ "github.com/mattn/go-sqlite3" -) - -var DB *sql.DB - -func InitializeDB() (*sql.DB, error) { - var err error - DB, err = sql.Open("sqlite3", "mydb.db") - return DB, err -} diff --git a/example/config/migrate.go b/example/config/migrate.go deleted file mode 100644 index bd71261..0000000 --- a/example/config/migrate.go +++ /dev/null @@ -1,29 +0,0 @@ -package config - -import ( - "database/sql" - "log" -) - -func Run() { - migrate(DB, UserTable) - migrate(DB, PostTable) - migrate(DB, CategoryTable) - migrate(DB, PostCategoryTable) - migrate(DB, PostRatingTable) - migrate(DB, PostRepliesTable) - migrate(DB, PostRepliesRatingTable) - migrate(DB, SessionTable) -} - -func migrate(db *sql.DB, query string) { - statement, err := db.Prepare(query) - if err == nil { - _, creationError := statement.Exec() - if creationError != nil { - log.Println(creationError.Error()) - } - } else { - log.Println(err.Error()) - } -} diff --git a/example/config/migrations.go b/example/config/migrations.go deleted file mode 100644 index cf7889e..0000000 --- a/example/config/migrations.go +++ /dev/null @@ -1,88 +0,0 @@ -package config - -const UserTable = ` -CREATE TABLE IF NOT EXISTS users ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - username TEXT NOT NULL UNIQUE, - age INTEGER DEFAULT 0, - gender TEXT, - firstname TEXT, - lastname TEXT, - email TEXT NOT NULL UNIQUE, - password TEXT NOT NULL, - provider TEXT NOT NULL -); -` - -const PostTable = ` -CREATE TABLE IF NOT EXISTS posts ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - title TEXT NOT NULL, - content TEXT NOT NULL, - created DATETIME DEFAULT CURRENT_TIMESTAMP, - user_id INTEGER, - foreign key (user_id) REFERENCES users (id) -); -` - -const CategoryTable = ` -CREATE TABLE IF NOT EXISTS categories ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - name TEXT NOT NULL, - name_slug TEXT NOT NULL -); -` - -const PostCategoryTable = ` -CREATE TABLE IF NOT EXISTS posts_category ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - post_id INTEGER, - category_id INTEGER, - foreign key (post_id) REFERENCES posts (id), - foreign key (category_id) REFERENCES categories (id) -); -` - -const PostRatingTable = ` -CREATE TABLE IF NOT EXISTS posts_rating ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - post_id INTEGER, - user_id INTEGER, - rating INTEGER, - foreign key (post_id) REFERENCES posts (id), - foreign key (user_id) REFERENCES users (id) -); -` - -const PostRepliesTable = ` -CREATE TABLE IF NOT EXISTS posts_replies ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - post_id INTEGER, - user_id INTEGER, - content TEXT, - created DATETIME DEFAULT CURRENT_TIMESTAMP, - foreign key (post_id) REFERENCES posts (id), - foreign key (user_id) REFERENCES users (id) -); -` -const PostRepliesRatingTable = ` -CREATE TABLE IF NOT EXISTS posts_replies_rating ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - post_reply_id INTEGER, - user_id INTEGER, - rating INTEGER, - foreign key (post_reply_id) REFERENCES posts_replies (id), - foreign key (user_id) REFERENCES users (id) -); -` - -const SessionTable = ` -CREATE TABLE IF NOT EXISTS sessions ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - user_id INTEGER, - name TEXT, - value TEXT, - expiration DATETIME, - foreign key (user_id) REFERENCES users (id) -); -` diff --git a/example/controller/categoryController.go b/example/controller/categoryController.go deleted file mode 100644 index b9eaeaf..0000000 --- a/example/controller/categoryController.go +++ /dev/null @@ -1,43 +0,0 @@ -package controller - -import ( - "encoding/json" - "fmt" - "net/http" - - "forum-authentication/types" -) - -type CategoryController struct{} - -func (_ *CategoryController) CategoryController(w http.ResponseWriter, r *http.Request) { - - category_slug := r.URL.Query().Get("slug") - - if r.Method == "GET" { - categories := []types.Categories{} - var err error - - if category_slug == "" { - categories, err = category.GetCategories() - } else { - categories, err = category.GetCategoryBySlug(category_slug) - } - - if err != nil { - fmt.Println(err) - http.Error(w, "Error getting categories", http.StatusInternalServerError) - return - } - - categoriesJson, err := json.Marshal(categories) - if err != nil { - http.Error(w, "Error encoding JSON", http.StatusInternalServerError) - return - } - - w.WriteHeader(http.StatusAccepted) - w.Write(categoriesJson) - - } -} diff --git a/example/controller/controllerHelpers.go b/example/controller/controllerHelpers.go deleted file mode 100644 index 04da8e0..0000000 --- a/example/controller/controllerHelpers.go +++ /dev/null @@ -1,28 +0,0 @@ -package controller - -import ( - "encoding/json" - "html/template" - "log" - "net/http" -) - -func RenderPage(w http.ResponseWriter, templatePath string, data interface{}) { - tmpl, err := template.ParseGlob(templatePath) - if err != nil { - log.Fatal(err) - } - - err = tmpl.Execute(w, data) - if err != nil { - log.Fatal(err) - } -} - -func RespondWithJSON(w http.ResponseWriter, status int, data interface{}) { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(status) - if err := json.NewEncoder(w).Encode(data); err != nil { - http.Error(w, "Error with encoding response", http.StatusInternalServerError) - } -} diff --git a/example/controller/homePageContoller.go b/example/controller/homePageContoller.go deleted file mode 100644 index ad27869..0000000 --- a/example/controller/homePageContoller.go +++ /dev/null @@ -1,230 +0,0 @@ -package controller - -import ( - "fmt" - "html/template" - "log" - "net/http" - - "forum-authentication/types" -) - -type HomePageController struct{} - -var ( - category types.Categories - postRating types.PostRating - postReply types.PostReply -) - -func (_ *HomePageController) HomePage(w http.ResponseWriter, r *http.Request) { - user, err := ValidateSession(w, r) - fmt.Println(user) - - data := struct { - SessionValid bool - Categories []types.Categories - CurrentCategory types.Categories - CurrentPost types.Post - CurrentPostReplies []types.PostReply - CurrentPostDislikes int - CurrentPostLikes int - - Posts []types.Post - }{ - SessionValid: err == nil, - Categories: []types.Categories{}, - CurrentCategory: types.Categories{}, - CurrentPost: types.Post{}, - CurrentPostReplies: []types.PostReply{}, - CurrentPostDislikes: 0, - CurrentPostLikes: 0, - Posts: []types.Post{}, - } - - // Check if the URL path is not root, return not found template - if r.URL.Path != "/" { - tmpl, err := template.ParseGlob("ui/templates/notFound.html") - if err != nil { - log.Fatal(err) - } - - err = tmpl.Execute(w, r) - return - } - - // Get all categories for the topics sidebar - categories, err := category.GetCategories() - if err != nil { - log.Println(err) - } - data.Categories = categories - - postID := r.URL.Query().Get("post") - filter := r.URL.Query().Get("filter") - categorySlug := r.URL.Query().Get("category") - - if postID != "" { - postID = r.URL.Query().Get("post") - category, err := category.GetCurrentCategory(categorySlug) - if err != nil { - renderNotFoundTemplate(w, r) - return - } - - data.CurrentCategory = category - currentPost, err := post.GetPostById(postID) - if err != nil { - renderNotFoundTemplate(w, r) - return - } - - dislikes, likes, err := postRating.GetPostRatings(postID) - if err != nil { - renderNotFoundTemplate(w, r) - return - } - content, err := postReply.GetPostReplies(postID) - - if err != nil { - renderNotFoundTemplate(w, r) - return - } - - data.CurrentPostReplies = content - data.CurrentPostDislikes = dislikes - data.CurrentPostLikes = likes - data.CurrentPost = currentPost - renderTemplate("ui/templates/post.html", w, data) - - } else if categorySlug != "" { - - category, err := category.GetCurrentCategory(categorySlug) - - if err != nil || category.Id == 0 { - renderNotFoundTemplate(w, r) - return - } - - data.CurrentCategory = category - - var posts []types.Post - - switch filter { - case "liked-posts": - user, err := ValidateSession(w, r) - - referer := r.Header.Get("referer") - - if err != nil { - http.Redirect(w, r, referer, http.StatusSeeOther) - return - } - posts, err = post.GetCategoryLikedPosts(category, user.Id) - if err != nil { - log.Println(err) - } - break - - case "created-posts": - user, err := ValidateSession(w, r) - - referer := r.Header.Get("referer") - - if err != nil { - http.Redirect(w, r, referer, http.StatusSeeOther) - return - } - posts, err = post.GetCategoryCreatedPosts(category, user.Id) - if err != nil { - log.Println(err) - } - break - - default: - posts, err = post.GetCategoryPosts(category) - } - - if err != nil || len(posts) == 0 { - log.Println(err) - } - - data.Posts = posts - renderTemplate("ui/templates/home.html", w, data) - - } else { - // when category or post id is not provided, return first category from the database - - data.CurrentCategory = categories[0] - data.Posts, err = post.GetCategoryPosts(categories[0]) - - if err != nil { - log.Println(err) - } - var posts []types.Post - category, err := category.GetCurrentCategory("python") - if err != nil { - log.Println(err) - } - - switch filter { - case "liked-posts": - user, err := ValidateSession(w, r) - - referer := r.Header.Get("referer") - - if err != nil { - http.Redirect(w, r, referer, http.StatusSeeOther) - return - } - posts, err = post.GetCategoryLikedPosts(category, user.Id) - if err != nil { - log.Println(err) - } - break - - case "created-posts": - user, err := ValidateSession(w, r) - - referer := r.Header.Get("referer") - - if err != nil { - http.Redirect(w, r, referer, http.StatusSeeOther) - return - } - posts, err = post.GetCategoryCreatedPosts(category, user.Id) - if err != nil { - log.Println(err) - } - break - - default: - posts, err = post.GetCategoryPosts(category) - } - - if err != nil || len(posts) == 0 { - log.Println(err) - } - - data.Posts = posts - renderTemplate("ui/templates/home.html", w, data) - } -} - -func renderNotFoundTemplate(w http.ResponseWriter, r *http.Request) { - tmpl, err := template.ParseGlob("ui/templates/notFound.html") - if err != nil { - log.Fatal(err) - } - - err = tmpl.Execute(w, r) -} - -func renderTemplate(templatePath string, w http.ResponseWriter, data interface{}) { - tmpl, err := template.ParseGlob(templatePath) - if err != nil { - log.Fatal(err) - } - - err = tmpl.Execute(w, data) -} diff --git a/example/controller/postController.go b/example/controller/postController.go deleted file mode 100644 index 65fd6d8..0000000 --- a/example/controller/postController.go +++ /dev/null @@ -1,102 +0,0 @@ -package controller - -import ( - "fmt" - "log" - "net/http" - "strconv" - - "forum-authentication/types" -) - -type PostController struct{} - -var post types.Post - -func (_ *PostController) CreatePost(w http.ResponseWriter, r *http.Request) { - _, err := ValidateSession(w, r) - - categories, err := category.GetCategories() - if err != nil { - log.Fatal(err) - } - - data := struct { - SessionValid bool - Categories []types.Categories - CurrentCategory types.Categories - }{ - SessionValid: err == nil, - Categories: categories, - CurrentCategory: category, - } - - switch r.Method { - case "GET": - - RenderPage(w, "ui/templates/createPost.html", data) - - case "POST": - - user, err := ValidateSession(w, r) - - referer := r.Header.Get("referer") - - if err != nil { - http.Redirect(w, r, referer, http.StatusSeeOther) - return - } - - if (user == types.User{}) { - http.Redirect(w, r, referer, http.StatusSeeOther) - return - } - - err = r.ParseForm() - if err != nil { - http.Error(w, "Invalid JSON", http.StatusBadRequest) - return - } - - title := r.FormValue("title") - content := r.FormValue("content") - categoryIDs := r.PostForm["categories"] - - for _, categoryIDStr := range categoryIDs { - categoryID, err := strconv.Atoi(categoryIDStr) - if err != nil { - fmt.Println(err) - http.Error(w, "Invalid category ID", http.StatusBadRequest) - return - } - - // Create a separate Post for each selected category - post := &types.Post{ - Title: title, - Content: content, - UserId: user.Id, - } - - postID, err := post.CreatePost(*post) - - if err != nil { - fmt.Println(err) - http.Error(w, "Error creating post", http.StatusInternalServerError) - return - } - - postsCategory := &types.PostCategories{ - CategoryId: categoryID, - PostId: int(postID), - } - _, err = category.CreatePostCategory(*&postsCategory) - if err != nil { - fmt.Println(err) - http.Error(w, "Error creating posts category", http.StatusInternalServerError) - return - } - http.Redirect(w, r, "/?post="+strconv.Itoa(int(postID)), http.StatusSeeOther) - } - - } -} diff --git a/example/controller/providers.go b/example/controller/providers.go deleted file mode 100644 index 23d8abc..0000000 --- a/example/controller/providers.go +++ /dev/null @@ -1,306 +0,0 @@ -package controller - -import ( - "encoding/json" - "errors" - "fmt" - "io" - "io/ioutil" - "net/http" - "net/url" - "strings" - - "forum-authentication/middleware" - "forum-authentication/types" -) - -const ( - google_ClientID = "89143036124-72c155p6tigsp9l1ch3ud520i9bho94f.apps.googleusercontent.com" - google_ClientSecret = "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" - google_RedirectURI = "http://localhost:8080/auth/google/callback" - - github_ClientID = "2ac7d35edf087740ae48" - github_ClientSecret = "425b0e0bacb5616b076f37f2be164b88698767d5" - github_RedirectURI = "http://localhost:8080/auth/github/callback" -) - -func exchangeCodeForToken(code, provider string) (access_token string, err error) { - var tokenExchangeUrl string - var format string - - params := url.Values{} - - switch provider { - case "google": - tokenExchangeUrl = "https://oauth2.googleapis.com/token" - params.Add("client_id", google_ClientID) - params.Add("client_secret", google_ClientSecret) - params.Add("redirect_uri", google_RedirectURI) - params.Add("grant_type", "authorization_code") - params.Add("code", code) - format = "application/x-www-form-urlencoded" - - resp, _ := http.Post(tokenExchangeUrl, format, strings.NewReader(params.Encode())) - if err != nil { - return "", err - } - - defer resp.Body.Close() - - bodyMap, err := convertBodyToJson(resp) - if err != nil { - return "", err - - } - - access_token = bodyMap["access_token"].(string) - return access_token, nil - - case "github": - tokenExchangeUrl = "https://github.com/login/oauth/access_token" - params.Add("client_id", github_ClientID) - params.Add("client_secret", github_ClientSecret) - params.Add("redirect_uri", github_RedirectURI) - params.Add("code", code) - format = "application/x-www-form-urlencoded" - - resp, err := http.Post(tokenExchangeUrl, format, strings.NewReader(params.Encode())) - if err != nil { - return "", err - } - - bodyBytes, err := io.ReadAll(resp.Body) - if err != nil { - return "", err - } - - body := string(bodyBytes) - parts := strings.Split(body, "&") - for _, part := range parts { - kv := strings.Split(part, "=") - if len(kv) == 2 && kv[0] == "access_token" { - access_token = kv[1] - break - } - } - - if access_token == "" { - return "", errors.New("Access token not found in response") - } - - return access_token, nil - - default: - return "", errors.New("Provider not found") - - } -} - -func GoogleGetUserProfile(access_token string, w http.ResponseWriter, r *http.Request) (user types.User, err error) { - userInfoUrl := "https://www.googleapis.com/oauth2/v1/userinfo" - params := url.Values{} - params.Add("access_token", access_token) - - response, err := http.Get(userInfoUrl + "?" + params.Encode()) - - if err != nil { - fmt.Println(err) - return - } - - data, err := convertBodyToJson(response) - - if err != nil { - fmt.Println(err) - return - } - - user = types.User{ - Username: data["name"].(string), - Email: data["email"].(string), - FirstName: data["given_name"].(string), - LastName: data["family_name"].(string), - Password: "FALSE", - Provider: "google", - } - - return user, nil -} -func GithubRequest(url, access_token string, target interface{}) error { - client := &http.Client{} - - req, err := http.NewRequest("GET", url, nil) - if err != nil { - return err - } - req.Header.Add("Authorization", "Bearer "+access_token) - - resp, err := client.Do(req) - if err != nil { - return err - } - defer resp.Body.Close() - - body, err := ioutil.ReadAll(resp.Body) - if err != nil { - return err - } - - if err := json.Unmarshal(body, target); err != nil { - return err - } - - return nil -} - -func GithubRequestEmail(url, access_token string) (string, error) { - var data []map[string]interface{} - if err := GithubRequest(url, access_token, &data); err != nil { - fmt.Println(err) - return "", err - } - - user_email := "" - for _, v := range data { - if v["primary"].(bool) { - user_email = v["email"].(string) - break - } - } - - return user_email, nil -} - -func GithubRequestUser(url, access_token string) (map[string]interface{}, error) { - var data map[string]interface{} - if err := GithubRequest(url, access_token, &data); err != nil { - fmt.Println(err) - return nil, err - } - - return data, nil -} - -func GithubGetUserProfile(access_token string, w http.ResponseWriter, r *http.Request) (user types.User, err error) { - var email string - var user_raw map[string]interface{} - - email, err = GithubRequestEmail("https://api.github.com/user/emails", access_token) - if err != nil { - fmt.Println(err) - return - } - - user_raw, err = GithubRequestUser("https://api.github.com/user", access_token) - user = types.User{ - Username: user_raw["login"].(string), - Email: email, - Password: "FALSE", - Provider: "github", - } - - return user, nil -} - -func (_ *UserController) AuthCallback(w http.ResponseWriter, r *http.Request) { - // endpoint: auth - - var provider string - path := r.URL.Path - - fmt.Println(path) - if strings.Contains(path, "google") { - provider = "google" - } else if strings.Contains(path, "github") { - provider = "github" - } else { - fmt.Println("Provider not found") - return - } - - code := r.URL.Query().Get("code") - if code == "" { - fmt.Println("Code not found") - return - } - - // exchange code for token - access_token, err := exchangeCodeForToken(code, provider) - if err != nil { - fmt.Println("Error exchanging code for access_token:", err) - return - } - - var user types.User - - switch provider { - case "google": - user, err = GoogleGetUserProfile(access_token, w, r) - break - - case "github": - fmt.Println(access_token) - user, err = GithubGetUserProfile(access_token, w, r) - break - } - - if err != nil { - fmt.Println("Error getting user profile:", err) - return - } - - var userId int - existingUser, err := user.GetUserByEmail(user.Email) - - if err != nil { - // if user does not exist, create user - userId, err = user.CreateUser(user, provider) - - if err != nil || userId == 0 { - fmt.Println("Error creating user:", err) - return - } - } else { - // if user exists, get user id - userId = existingUser.Id - } - - cookie := middleware.GenerateCookie(w, r, userId) - - http.SetCookie(w, &cookie) - http.Redirect(w, r, "/", http.StatusSeeOther) -} - -func (_ *UserController) GoogleAuth(w http.ResponseWriter, r *http.Request) { - - switch r.Method { - - case "GET": - // endpoint: auth/google - authURL := "https://accounts.google.com/o/oauth2/auth" - params := url.Values{} - fmt.Println(google_RedirectURI) - params.Add("client_id", google_ClientID) - params.Add("redirect_uri", google_RedirectURI) - params.Add("scope", "profile email") - params.Add("response_type", "code") - - http.Redirect(w, r, authURL+"?"+params.Encode(), http.StatusFound) - } -} - -func (_ *UserController) GithubAuth(w http.ResponseWriter, r *http.Request) { - - switch r.Method { - - case "GET": - // endpoint: auth/github - authURL := "https://github.com/login/oauth/authorize" - params := url.Values{} - params.Add("client_id", github_ClientID) - params.Add("redirect_uri", github_RedirectURI) - params.Add("scope", "user user:email") - - http.Redirect(w, r, authURL+"?"+params.Encode(), http.StatusFound) - } -} diff --git a/example/controller/ratingController.go b/example/controller/ratingController.go deleted file mode 100644 index 480d628..0000000 --- a/example/controller/ratingController.go +++ /dev/null @@ -1,74 +0,0 @@ -package controller - -import ( - "net/http" - "strconv" - - "forum-authentication/types" -) - -type RatingController struct{} - -func (_ *RatingController) RatingController(w http.ResponseWriter, r *http.Request) { - - var replyRating types.ReplyRating - - if r.Method == "POST" { - user, err := ValidateSession(w, r) - referer := r.Header.Get("Referer") - - if err != nil { - http.Redirect(w, r, referer, http.StatusSeeOther) - return - } - - if (user == types.User{}) { - http.Redirect(w, r, referer, http.StatusSeeOther) - return - } - - post_id_string := r.URL.Query().Get("post_id") - rating_id_string := r.URL.Query().Get("rating_id") - - // Define a common function to process ratings. - processRating := func(id int, rating string, handleFunc func(int, int, string)) { - if id == 0 { - http.Error(w, "Invalid ID", http.StatusBadRequest) - return - } - - err := r.ParseForm() - if err != nil { - http.Error(w, "Invalid JSON", http.StatusBadRequest) - return - } - - ratingValue := r.FormValue("rating") - if user.Id == 0 || ratingValue == "" { - http.Error(w, "Missing parameters", http.StatusBadRequest) - return - } - - handleFunc(id, user.Id, ratingValue) - http.Redirect(w, r, referer, http.StatusSeeOther) - } - - if post_id_string != "" { - postID, err := strconv.Atoi(post_id_string) - - if err != nil { - http.Error(w, "Invalid post_id", http.StatusBadRequest) - return - } - processRating(postID, "rating", postRating.HandlePostRating) - - } else if rating_id_string != "" { - ratingID, err := strconv.Atoi(rating_id_string) - if err != nil { - http.Error(w, "Invalid rating_id", http.StatusBadRequest) - return - } - processRating(ratingID, "rating", replyRating.HandleReplyRating) - } - } -} diff --git a/example/controller/replyController.go b/example/controller/replyController.go deleted file mode 100644 index d37ddf1..0000000 --- a/example/controller/replyController.go +++ /dev/null @@ -1,54 +0,0 @@ -package controller - -import ( - "net/http" - "strconv" - - "forum-authentication/types" -) - -type ReplyController struct{} - -func (_ *ReplyController) ReplyController(w http.ResponseWriter, r *http.Request) { - var postReply types.PostReply - - switch r.Method { - - case "POST": - user, err := ValidateSession(w, r) - referer := r.Header.Get("referer") - - if err != nil { - http.Redirect(w, r, referer, http.StatusSeeOther) - return - } - - if (user == types.User{}) { - http.Redirect(w, r, referer, http.StatusSeeOther) - return - } - - post_id_string := r.URL.Query().Get("post_id") - - post_id, err := strconv.Atoi(post_id_string) - if err != nil { - post_id = 0 - } - - err = r.ParseForm() - if err != nil { - http.Error(w, "Invalid JSON", http.StatusBadRequest) - return - } - - content := r.FormValue("content") - if user.Id == 0 || post_id == 0 || content == "" { - http.Error(w, "Missing parameters", http.StatusBadRequest) - return - } - - postReply.CreatePostReply(post_id, user.Id, content) - - http.Redirect(w, r, "/?post="+post_id_string, http.StatusSeeOther) - } -} diff --git a/example/controller/userController.go b/example/controller/userController.go deleted file mode 100644 index 614f3f8..0000000 --- a/example/controller/userController.go +++ /dev/null @@ -1,202 +0,0 @@ -package controller - -import ( - "encoding/base64" - "encoding/json" - "errors" - "fmt" - "io/ioutil" - "net/http" - "strconv" - "strings" - - "golang.org/x/crypto/bcrypt" - - "forum-authentication/middleware" - "forum-authentication/types" -) - -type Error struct { - Message string - SessionValid bool -} - -type UserController struct{} - -var ( - user types.User -) - -func (_ *UserController) CreateUser(w http.ResponseWriter, r *http.Request) { - - switch r.Method { - - case "GET": - _, err := ValidateSession(w, r) - RenderPage(w, "ui/templates/signup.html", Error{ - SessionValid: err == nil, - }) - - case "POST": - err := r.ParseForm() - if err != nil { - http.Error(w, "Invalid JSON", http.StatusBadRequest) - return - } - - ageStr := r.FormValue("age") - age, err := strconv.Atoi(ageStr) - if err != nil { - http.Error(w, "Invalid age value", http.StatusBadRequest) - return - } - - user := types.User{ - Username: r.FormValue("username"), - Age: age, - Gender: r.FormValue("gender"), - FirstName: r.FormValue("first_name"), - LastName: r.FormValue("last_name"), - Email: r.FormValue("email"), - Password: r.FormValue("password"), - } - - hashedPassword, err := bcrypt.GenerateFromPassword([]byte(user.Password), bcrypt.DefaultCost) - if err != nil { - fmt.Println("Error hashing the password:", err) - return - } - user.Password = string(hashedPassword) - - userID, err := user.CreateUser(user, "password") - er := Error{ - Message: "Email or Username already taken", - } - if err != nil || userID == 0 { - - RenderPage(w, "ui/templates/signup.html", er) - - return - } - - http.Redirect(w, r, "/", http.StatusSeeOther) - } -} - -func convertBodyToJson(r *http.Response) (map[string]interface{}, error) { - body, err := ioutil.ReadAll(r.Body) - if err != nil { - return nil, err - } - - // convert body to map - var bodyMap map[string]interface{} - err = json.Unmarshal(body, &bodyMap) - if err != nil { - return nil, err - } - - return bodyMap, nil -} - -func (_ *UserController) Login(w http.ResponseWriter, r *http.Request) { - - switch r.Method { - - case "GET": - - RenderPage(w, "ui/templates/login.html", nil) - - case "POST": - - if r.Method != http.MethodPost { - http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) - return - } - - username := r.FormValue("username") - password := r.FormValue("password") - - user, err := user.CheckCredentials(username, password) - - if user.Provider != "password" { - er := Error{ - Message: "Incorrect Username or Password", - } - RenderPage(w, "ui/templates/login.html", er) - } - - er := Error{ - Message: "Incorrect Username or Password", - } - - if err != nil { - RenderPage(w, "ui/templates/login.html", er) - } - - cookie := middleware.GenerateCookie(w, r, user.Id) - - http.SetCookie(w, &cookie) - http.Redirect(w, r, "/", http.StatusSeeOther) - - default: - http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) - return - } -} - -func (_ *UserController) Logout(w http.ResponseWriter, r *http.Request) { - middleware.ClearSession(w, r) - http.Redirect(w, r, "/", http.StatusSeeOther) -} - -func (_ *UserController) ProfilePage(w http.ResponseWriter, r *http.Request) { - - user, err := ValidateSession(w, r) - if (err != nil || user == types.User{}) { - http.Redirect(w, r, "/login", http.StatusSeeOther) - return - } - - RenderPage(w, "ui/templates/userProfile.html", user) -} - -func ValidateSession(w http.ResponseWriter, r *http.Request) (user types.User, err error) { - user = types.User{} - - cookie, err := r.Cookie("session-1") - if err != nil { - return user, err - } - - decodedCookie, err := base64.StdEncoding.DecodeString(cookie.Value) - if err != nil { - errors.New("Error decoding cookie") - return - } - cookieValues := strings.Split(string(decodedCookie), "::") - - if len(cookieValues) != 2 { - return user, errors.New("Invalid cookie value") - } - - session_id := cookieValues[0] - useragent := cookieValues[1] - - if useragent != r.Header.Get("User-Agent") { - fmt.Println("User agent mismatch") - return user, errors.New("Invalid user agent") - } - - user, err = user.GetUserFromSession(session_id) - - if err != nil { - return user, err - } - - if user.Id == 0 { - return user, nil - } - - return user, nil -} diff --git a/example/dockerfile b/example/dockerfile deleted file mode 100644 index d233337..0000000 --- a/example/dockerfile +++ /dev/null @@ -1,12 +0,0 @@ -FROM golang:1.21 - -ADD . /go/src/app -WORKDIR /go/src/app - -RUN go get forum-authentication -RUN go build -o app -RUN go install - -EXPOSE 8080 - -CMD ["./app"] \ No newline at end of file diff --git a/example/dto/userResponseDto.go b/example/dto/userResponseDto.go deleted file mode 100644 index 73816bf..0000000 --- a/example/dto/userResponseDto.go +++ /dev/null @@ -1,24 +0,0 @@ -package dto - -import ( - "forum-authentication/types" -) - -type UserDTO struct { - Id int - Username string - FirstName string - LastName string - Email string -} - -// NewUserDTO creates a UserDTO from a User, excluding sensitive fields. -func NewUserDTO(user types.User) UserDTO { - return UserDTO{ - Id: user.Id, - Username: user.Username, - FirstName: user.FirstName, - LastName: user.LastName, - Email: user.Email, - } -} diff --git a/example/go.mod b/example/go.mod deleted file mode 100644 index 676bba1..0000000 --- a/example/go.mod +++ /dev/null @@ -1,10 +0,0 @@ -module forum-authentication - -go 1.21 - -require github.com/mattn/go-sqlite3 v1.14.17 - -require ( - github.com/google/uuid v1.3.1 // indirect - golang.org/x/crypto v0.14.0 // indirect -) diff --git a/example/go.sum b/example/go.sum deleted file mode 100644 index d55b312..0000000 --- a/example/go.sum +++ /dev/null @@ -1,6 +0,0 @@ -github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= -github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/mattn/go-sqlite3 v1.14.17 h1:mCRHCLDUBXgpKAqIKsaAaAsrAlbkeomtRFKXh2L6YIM= -github.com/mattn/go-sqlite3 v1.14.17/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= -golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= -golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= diff --git a/example/main.go b/example/main.go deleted file mode 100644 index 3ee0d20..0000000 --- a/example/main.go +++ /dev/null @@ -1,26 +0,0 @@ -package main - -import ( - "log" - - "forum-authentication/config" - "forum-authentication/server" - - _ "github.com/mattn/go-sqlite3" -) - -func init() { - _, err := config.InitializeDB() - if err != nil { - log.Println("Driver creation failed", err.Error()) - } - - config.Run() -} - -func main() { - - server := server.NewServer(":8080") - log.Fatal(server.Start()) - -} diff --git a/example/middleware/cookieStuff.go b/example/middleware/cookieStuff.go deleted file mode 100644 index d0d72ea..0000000 --- a/example/middleware/cookieStuff.go +++ /dev/null @@ -1,66 +0,0 @@ -package middleware - -import ( - "encoding/base64" - "errors" - "fmt" - "math/rand" - "net/http" - "time" - - "forum-authentication/config" -) - -var ( - ErrValueTooLong = errors.New("cookie value too long") - ErrInvalidValue = errors.New("invalid cookie value") -) - -func GenerateRandomString(length int) string { - const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" - seededRand := rand.New(rand.NewSource(time.Now().UnixNano())) - result := make([]byte, length) - for i := range result { - result[i] = charset[seededRand.Intn(len(charset))] - } - return string(result) -} - -func GenerateCookie(w http.ResponseWriter, r *http.Request, userID int) http.Cookie { - - expiration := time.Now().Add(1 * time.Hour) - random_string := GenerateRandomString(32) - user_agent := r.Header.Get("User-Agent") - - encodedData := base64.StdEncoding.EncodeToString([]byte(random_string + "::" + user_agent)) - - cookie := http.Cookie{ - Name: "session-1", - Value: encodedData, - Expires: expiration, - Path: "/", - Secure: true, - HttpOnly: true, - } - - _, err := config.DB.Exec("INSERT INTO sessions (user_id, name, value, expiration) VALUES (?, ?, ?, ?)", userID, cookie.Name, random_string, cookie.Expires) - if err != nil { - fmt.Println("failed", err) - return http.Cookie{} - } - - return cookie -} - -func ClearSession(w http.ResponseWriter, r *http.Request) { - cookie := http.Cookie{ - Name: "session-1", - Value: "", - Expires: time.Now(), - Path: "/", - Secure: true, - HttpOnly: true, - } - - http.SetCookie(w, &cookie) -} diff --git a/example/mydb-db b/example/mydb-db deleted file mode 100644 index e69de29..0000000 diff --git a/example/server/server.go b/example/server/server.go deleted file mode 100644 index 8068447..0000000 --- a/example/server/server.go +++ /dev/null @@ -1,60 +0,0 @@ -package server - -import ( - "fmt" - "net/http" - - "forum-authentication/controller" -) - -var ( - userController controller.UserController - postController controller.PostController - categoryController controller.CategoryController - homePageController controller.HomePageController - handleRatingController controller.RatingController - handleReplyController controller.ReplyController -) - -type Server struct { - ListenAddress string -} - -func NewServer(listenAddr string) *Server { - return &Server{ - ListenAddress: listenAddr, - } -} - -func (s *Server) Start() error { - http.HandleFunc("/signup", userController.CreateUser) - - http.HandleFunc("/login", userController.Login) - - http.HandleFunc("/logout", userController.Logout) - - http.HandleFunc("/me", userController.ProfilePage) - - http.HandleFunc("/create", postController.CreatePost) - - http.HandleFunc("/category", categoryController.CategoryController) - - http.HandleFunc("/handle-rating", handleRatingController.RatingController) - - http.HandleFunc("/handle-reply", handleReplyController.ReplyController) - - http.HandleFunc("/auth/google", userController.GoogleAuth) - - http.HandleFunc("/auth/google/callback", userController.AuthCallback) - - http.HandleFunc("/auth/github", userController.GithubAuth) - - http.HandleFunc("/auth/github/callback", userController.AuthCallback) - - http.HandleFunc("/", homePageController.HomePage) - - http.Handle("/assets/", http.StripPrefix("/assets/", http.FileServer(http.Dir("ui/assets")))) - - fmt.Println("Server running at port", s.ListenAddress) - return http.ListenAndServe(s.ListenAddress, nil) -} diff --git a/example/types/category.go b/example/types/category.go deleted file mode 100644 index dbc6368..0000000 --- a/example/types/category.go +++ /dev/null @@ -1,94 +0,0 @@ -package types - -import ( - "database/sql" - - "forum-authentication/config" -) - -type Categories struct { - Id int - Name string - Name_slug string -} - -type PostCategories struct { - Id int - PostId int - CategoryId int -} - -func (c *Categories) GetCategories() ([]Categories, error) { - var categories []Categories - stmt := `SELECT * FROM categories` - - res, err := config.DB.Query(stmt) - if err != nil { - return nil, err - } - - defer res.Close() - - for res.Next() { - var category Categories - if err := res.Scan(&category.Id, &category.Name, &category.Name_slug); err != nil { - return nil, err - } - categories = append(categories, category) - } - - if err := res.Err(); err != nil { - return nil, err - } - - return categories, nil -} - -func (c *Categories) GetCategoryBySlug(slug string) ([]Categories, error) { - var categories []Categories - stmt := `SELECT * FROM categories WHERE name_slug = ?` - - err := config.DB.QueryRow(stmt, slug).Scan(&c.Id, &c.Name, &c.Name_slug) - - categories = append(categories, *c) - - if err != nil { - if err == sql.ErrNoRows { - return categories, err - } - return categories, err - } - return categories, nil -} - -func (c *Categories) GetCurrentCategory(cat string) (Categories, error) { - if cat != "" { - categories, err := c.GetCategoryBySlug(cat) - if err != nil || len(categories) == 0 { - return *c, err - } - return categories[0], nil - } - return *c, nil -} - -func (c *Categories) CreatePostCategory(postCategories *PostCategories) (int64, error) { - insertStmt := `INSERT INTO posts_category (post_id, category_id) VALUES (?, ?)` - - stmt, err := config.DB.Prepare(insertStmt) - if err != nil { - return 0, err - } - - result, err := stmt.Exec(postCategories.PostId, postCategories.CategoryId) - if err != nil { - return 0, err - } - - postCategoryID, err := result.LastInsertId() - if err != nil { - return 0, err - } - - return postCategoryID, nil -} diff --git a/example/types/post.go b/example/types/post.go deleted file mode 100644 index b3a9c95..0000000 --- a/example/types/post.go +++ /dev/null @@ -1,430 +0,0 @@ -package types - -import ( - "database/sql" - "time" - - "forum-authentication/config" -) - -type Post struct { - Id int - Title string - Content string - Created time.Time - UserId int - Username string -} - -type PostRating struct { - Id int - PostId int - UserId int - Rating int -} - -type PostReply struct { - Id int - PostId int - UserId int - Username string - Content string - Created time.Time - Dislikes int - Likes int -} - -type ReplyRating struct { - Id int - PostReplyId int - UserId int - Rating int -} - -func (p *Post) CreatePost(post Post) (int64, error) { - - insertStmt := `INSERT INTO posts (title, content, user_id) VALUES (?, ?, ?)` - - stmt, err := config.DB.Prepare(insertStmt) - if err != nil { - return 0, err - } - - result, err := stmt.Exec(post.Title, post.Content, post.UserId) - if err != nil { - return 0, err - } - - postID, err := result.LastInsertId() - if err != nil { - return 0, err - } - - return postID, nil -} - -func (p *Post) GetCategoryPosts(category Categories) ([]Post, error) { - - if category.Id == 0 { - return nil, nil - } - - stmt := ` - SELECT posts.* - FROM posts - JOIN posts_category ON posts.id = posts_category.post_id - WHERE posts_category.category_id = ? - ` - - var posts []Post - res, err := config.DB.Query(stmt, category.Id) - if err != nil { - panic(err) - } - - defer res.Close() - - for res.Next() { - var post Post - - err = res.Scan(&post.Id, &post.Title, &post.Content, &post.Created, &post.UserId) - if err != nil { - panic(err) - } - - posts = append(posts, post) - } - - if err := res.Err(); err != nil { - return nil, err - } - - return posts, nil -} - -func (p *Post) GetCategoryLikedPosts(category Categories, user_id int) ([]Post, error) { - if category.Id == 0 { - return nil, nil - } - - stmt := ` - SELECT posts.* - FROM posts - JOIN posts_category ON posts.id = posts_category.post_id - JOIN posts_rating ON posts.id = posts_rating.post_id - WHERE posts_category.category_id = ? AND posts_rating.user_id = ? AND posts_rating.rating = 1 - ` - - var posts []Post - res, err := config.DB.Query(stmt, category.Id, user_id) - if err != nil { - panic(err) - } - - defer res.Close() - - for res.Next() { - var post Post - err = res.Scan(&post.Id, &post.Title, &post.Content, &post.Created, &post.UserId) - if err != nil { - panic(err) - } - - posts = append(posts, post) - } - - if err := res.Err(); err != nil { - return nil, err - } - - return posts, nil -} - -func (p *Post) GetCategoryCreatedPosts(category Categories, user_id int) ([]Post, error) { - if category.Id == 0 { - return nil, nil - } - - stmt := ` - SELECT posts.* - FROM posts - JOIN posts_category ON posts.id = posts_category.post_id - WHERE posts_category.category_id = ? AND posts.user_id = ? - ` - - var posts []Post - res, err := config.DB.Query(stmt, category.Id, user_id) - if err != nil { - panic(err) - } - - defer res.Close() - - for res.Next() { - var post Post - err = res.Scan(&post.Id, &post.Title, &post.Content, &post.Created, &post.UserId) - if err != nil { - panic(err) - } - - posts = append(posts, post) - } - - if err := res.Err(); err != nil { - return nil, err - } - - return posts, nil -} - -func (p *Post) GetPostById(id string) (Post, error) { - var post Post - stmt := ` - SELECT posts.*, u.Username - FROM posts - JOIN Users u ON posts.user_id = u.id - WHERE posts.id = ? - ` - - err := config.DB.QueryRow(stmt, id).Scan(&post.Id, &post.Title, &post.Content, &post.Created, &post.UserId, &post.Username) - - if err != nil { - if err == sql.ErrNoRows { - return post, err - } - return post, err - } - return post, nil -} - -func (p *PostRating) HandlePostRating(id, user_id int, rating string) { - stmt := `SELECT * FROM posts_rating WHERE post_id = ? AND user_id = ?` - err := config.DB.QueryRow(stmt, id, user_id).Scan(&p.Id, &p.PostId, &p.UserId, &p.Rating) - - if err != nil { - if err == sql.ErrNoRows { - p.CreatePostRating(id, user_id, rating) - } - } - - p.UpdatePostRating(id, user_id, rating) -} - -func (p *ReplyRating) HandleReplyRating(id, user_id int, rating string) { - stmt := `SELECT * FROM posts_replies_rating WHERE post_reply_id = ? AND user_id = ?` - err := config.DB.QueryRow(stmt, id, user_id).Scan(&p.Id, &p.PostReplyId, &p.UserId, &p.Rating) - if err != nil { - if err == sql.ErrNoRows { - p.CreateReplyRating(id, user_id, rating) - } - } - - p.UpdateReplyRating(id, user_id, rating) -} - -func (p *ReplyRating) CreateReplyRating(id int, user_id int, rating string) (int64, error) { - insertStmt := `INSERT INTO posts_replies_rating (post_reply_id, user_id, rating) VALUES (?, ?, ?)` - stmt, err := config.DB.Prepare(insertStmt) - if err != nil { - return 0, err - } - - result, err := stmt.Exec(id, user_id, rating) - if err != nil { - return 0, err - } - - postID, err := result.LastInsertId() - if err != nil { - return 0, err - } - - return postID, nil -} - -func (p *ReplyRating) UpdateReplyRating(id, user_id int, rating string) (int64, error) { - updateStmt := `UPDATE posts_replies_rating SET rating = ? WHERE post_reply_id = ? AND user_id = ?` - - stmt, err := config.DB.Prepare(updateStmt) - if err != nil { - return 0, err - } - - result, err := stmt.Exec(rating, id, user_id) - if err != nil { - return 0, err - } - - postID, err := result.LastInsertId() - if err != nil { - return 0, err - } - - return postID, nil -} - -func (p *PostReply) GetReplyRatings(id int) (int, int, error) { - /* - Iterates through all the ratings for a post and returns the number of likes and dislikes - */ - stmt := `SELECT * FROM posts_replies_rating WHERE post_reply_id = ?` - - dislikes := 0 - likes := 0 - - res, err := config.DB.Query(stmt, id) - if err != nil { - panic(err) - } - - defer res.Close() - - for res.Next() { - var replyRating ReplyRating - err = res.Scan(&replyRating.Id, &replyRating.PostReplyId, &replyRating.UserId, &replyRating.Rating) - - if err != nil { - panic(err) - } - - if replyRating.Rating == 0 { - dislikes++ - } else { - likes++ - } - } - - return dislikes, likes, err -} - -func (p *PostRating) CreatePostRating(id int, user_id int, rating string) (int64, error) { - insertStmt := `INSERT INTO posts_rating (post_id, user_id, rating) VALUES (?, ?, ?)` - - stmt, err := config.DB.Prepare(insertStmt) - if err != nil { - return 0, err - } - - result, err := stmt.Exec(id, user_id, rating) - if err != nil { - return 0, err - } - - postID, err := result.LastInsertId() - if err != nil { - return 0, err - } - - return postID, nil -} - -func (p *PostRating) UpdatePostRating(id, user_id int, rating string) (int64, error) { - - updateStmt := `UPDATE posts_rating SET rating = ? WHERE post_id = ? AND user_id = ?` - - stmt, err := config.DB.Prepare(updateStmt) - if err != nil { - return 0, err - } - - result, err := stmt.Exec(rating, id, user_id) - if err != nil { - return 0, err - } - - postID, err := result.LastInsertId() - if err != nil { - return 0, err - } - - return postID, nil -} - -func (p *PostRating) GetPostRatings(id string) (int, int, error) { - /* - Iterates through all the ratings for a post and returns the number of likes and dislikes - */ - stmt := `SELECT * FROM posts_rating WHERE post_id = ?` - - dislikes := 0 - likes := 0 - - res, err := config.DB.Query(stmt, id) - if err != nil { - panic(err) - } - - defer res.Close() - - for res.Next() { - var postRating PostRating - err = res.Scan(&postRating.Id, &postRating.PostId, &postRating.UserId, &postRating.Rating) - - if err != nil { - panic(err) - } - - if postRating.Rating == 0 { - dislikes++ - } else { - likes++ - } - } - - return dislikes, likes, err -} - -func (p *PostReply) CreatePostReply(id int, user_id int, content string) (int64, error) { - insertStmt := `INSERT INTO posts_replies (post_id, user_id, content, created) VALUES (?, ?, ?, datetime('now', 'localtime'))` - - stmt, err := config.DB.Prepare(insertStmt) - if err != nil { - return 0, err - } - - result, err := stmt.Exec(id, user_id, content) - if err != nil { - return 0, err - } - - postID, err := result.LastInsertId() - if err != nil { - return 0, err - } - - return postID, nil -} - -func (p *PostReply) GetPostReplies(id string) ([]PostReply, error) { - /* - Iterates through all the ratings for a post and returns the number of likes and dislikes - */ - stmt := `SELECT pr.*, u.username FROM posts_replies pr JOIN Users u ON pr.user_id = u.id WHERE pr.post_id = ?` - - var postReplies []PostReply - res, err := config.DB.Query(stmt, id) - if err != nil { - panic(err) - } - - defer res.Close() - - for res.Next() { - var postReply PostReply - - if err != nil { - panic(err) - } - - err = res.Scan(&postReply.Id, &postReply.PostId, &postReply.UserId, &postReply.Content, &postReply.Created, &postReply.Username) - dislikes, likes, err := p.GetReplyRatings(postReply.Id) - postReply.Dislikes = dislikes - postReply.Likes = likes - - if err != nil { - panic(err) - } - postReplies = append(postReplies, postReply) - } - return postReplies, err -} diff --git a/example/types/user.go b/example/types/user.go deleted file mode 100644 index 817568a..0000000 --- a/example/types/user.go +++ /dev/null @@ -1,100 +0,0 @@ -package types - -import ( - "database/sql" - "fmt" - - "forum-authentication/config" - - "golang.org/x/crypto/bcrypt" -) - -type User struct { - Id int `json:"id"` - Username string `json:"username"` - Age int `json:"age"` - Gender string `json:"gender"` - FirstName string `json:"first_name"` - LastName string `json:"last_name"` - Email string `json:"email"` - Password string `json:"password"` - Provider string `json:"provider"` -} - -func (u *User) CreateUser(user User, provider string) (int, error) { - insertStmt := `INSERT INTO users (username, age, gender, firstname, lastname, email, password, provider) VALUES (?, ?, ?, ?, ?, ?, ?, ?)` - - stmt, err := config.DB.Prepare(insertStmt) - if err != nil { - return 0, err - } - fmt.Println("provider") - result, err := stmt.Exec(user.Username, user.Age, user.Gender, user.FirstName, user.LastName, user.Email, user.Password, provider) - if err != nil { - return 0, err - } - - userID, err := result.LastInsertId() - if err != nil { - return 0, err - } - - return int(userID), nil -} - -func (u *User) GetUserByUsername(username string) (User, error) { - stmt := `SELECT * FROM users WHERE username=?` - - err := config.DB.QueryRow(stmt, username).Scan(&u.Id, &u.Username, &u.Age, &u.Gender, &u.FirstName, &u.LastName, &u.Email, &u.Password, &u.Provider) - if err != nil { - if err == sql.ErrNoRows { - return *u, fmt.Errorf("User not found") - } - return *u, err - } - return *u, nil -} - -func (u *User) GetUserByEmail(email string) (User, error) { - stmt := `SELECT * FROM users WHERE email=?` - - err := config.DB.QueryRow(stmt, email).Scan(&u.Id, &u.Username, &u.Age, &u.Gender, &u.FirstName, &u.LastName, &u.Email, &u.Password, &u.Provider) - if err != nil { - if err == sql.ErrNoRows { - return *u, fmt.Errorf("User not found") - } - return *u, err - } - return *u, nil -} - -func (u *User) GetUserFromSession(value string) (User, error) { - stmt := ` - SELECT users.id, users.username, users.age, users.gender, users.firstname, users.lastname, users.email, users.password - FROM sessions - JOIN users ON sessions.user_id = users.id - WHERE sessions.value = ? - ` - err := config.DB.QueryRow(stmt, value).Scan(&u.Id, &u.Username, &u.Age, &u.Gender, &u.FirstName, &u.LastName, &u.Email, &u.Password) - if err != nil { - if err == sql.ErrNoRows { - return User{}, fmt.Errorf("User not found") - } - return User{}, err - } - return *u, nil -} - -func (u *User) CheckCredentials(username, password string) (User, error) { - user, err := u.GetUserByUsername(username) - if err != nil { - return User{}, err - } - err = bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(password)) - - if err != nil { - return User{}, err - } - - return user, nil -} diff --git a/example/ui/assets/images/github.png b/example/ui/assets/images/github.png deleted file mode 100644 index 9490ffc..0000000 Binary files a/example/ui/assets/images/github.png and /dev/null differ diff --git a/example/ui/assets/images/google.png b/example/ui/assets/images/google.png deleted file mode 100644 index a395697..0000000 Binary files a/example/ui/assets/images/google.png and /dev/null differ diff --git a/example/ui/assets/images/logo.png b/example/ui/assets/images/logo.png deleted file mode 100644 index 595e995..0000000 Binary files a/example/ui/assets/images/logo.png and /dev/null differ diff --git a/example/ui/assets/style.css b/example/ui/assets/style.css deleted file mode 100644 index 6ca1b5c..0000000 --- a/example/ui/assets/style.css +++ /dev/null @@ -1,739 +0,0 @@ -html { - font-family: Helvetica, Arial, sans-serif; -} - - -header { - background-color: white; - border-bottom: 1px solid rgba(128, 128, 128, 0.3); -} - -header ul { - display: flex; - justify-content: flex-end; - margin-right: 55px; - align-items: center; -} - -header li { - list-style-type: none; -} - -header li a { - padding: 10px; - text-decoration: none; - margin: 10px; -} - -header li a:hover { - color: red; - text-decoration:none; -} - -header li a, a:visited { - text-decoration: none; - color:inherit; -} - -.topnav { - display: flex; - justify-content: space-between; - height: 60px; -} - -body { - width: 100%; - margin: 0px; - font-size: 18px; -} - -.home { - display: flex; - height: 100%; - flex-direction: row-reverse; -} - -.sidebar { - width: 20vw; - height: 100vh; - background-color: white; - margin-top: 20px; -} - -.sidebar p { - text-align: center; - margin-left: 8px; - font-weight: 350; - margin-top: 13px; - margin-bottom: 15px; - -} - - -.category-container { - - width: 80vw; - min-height: fit-content; - background-color: white; - border-left: 1px solid rgba(128, 128, 128, 0.301); - -} - -.categories { - display: flex; - flex-direction: column; - gap:10px; - align-items: center; - padding: 0; -} - -.categories li { - color: rgb(246, 71, 27); - padding: 0; - width: 100%; - list-style: inside; - text-align: center; -} - - - -.categories li:hover { - background-color: rgba(149, 149, 149, 0.275); -} - -.category a, a:visited{ - color: inherit; -} - - -.categories li span { - color: black; - font-weight: 250; -} - -.category { - list-style-type: square; -} - -.category a { - text-decoration: none; -} - - -.filters { - margin-top: 30px; - display: flex; - flex-direction: row; - gap: 10px; - margin-left: 70px; - margin-right: 70px; -} - -.filters .select select { - padding: 5px; - margin-bottom: 10px; -} - -.headers { - display: flex; - flex-direction: row; - justify-content: space-between; - border-bottom: 2px solid rgba(128, 128, 128, 0.301); - margin: 0px 70px 0px 70px; -} - -.right { - display: flex; - flex-direction: row; - gap: 20px; - font-weight: lighter; -} - - -.right h3, .left h3 { - font-weight: lighter; -} - -.post { - display: flex; - flex-direction: row; - justify-content: space-between; - padding-bottom: 15px; - padding-top: 15px; - color: #9c1007; - border-bottom: 1px solid rgba(128, 128, 128, 0.3); -} - -.posts { - margin: 20px 70px 0 70px; -} - -.posts .right { - gap: 55px; -} - -img { - object-fit: contain; - padding: 10px; - margin-left: 6%; - -} - - -#login-container { - margin: auto; - border: 1px solid black; - width: 400px; - height: 455px; - text-align: center; - border-radius: 20px; - background-color: white; - -} - -.login-main { - height: 80vh; - display: flex; - justify-content: center; - -} - -.input-area { - display: flex; - flex-direction: column; - padding: 10px; - gap: 5px; -} - -.login input { - width: 60%; - margin: auto; - border-radius: 5px; - border: 1px solid rgba(0, 0, 0, 0.396); - padding: 3px; -} - -.signup-link { - margin-top: 50px; -} - -.signup-link a { - text-decoration: none; -} - -.signup-link a, a:visited { - color: inherit; - font-weight: bold; -} - -.signup-link a:hover { - color: rgb(75, 162, 238) -} - -.login-button { - margin: 10px; - width: 60%; - padding: 5px 0px 5px 0px; - background-color: rgb(84, 121, 240); - border-radius: 5px; - border: none; - -} - -.login-button:hover { - cursor: pointer; - background-color: rgb(66, 86, 159); -} - - - -.signup-container .input-area { - padding: 0px; - height: 80px -} - - -.signup-main { - display: flex; - justify-content: center; - align-items: center; - height: 80vh; -} - -.signup-container { - margin-top: 100px; - display: flex; - flex-direction: column; - height: 700px; - border-radius: 20px; - border: 1px solid black; - width: 500px; - padding-top: 20px; - padding-bottom: 20px; -} - -.signup-container input, .signup-container select { - width: 60%; - margin: auto; - border-radius: 5px; - border: 1px solid rgba(0, 0, 0, 0.396); - padding: 3px; -} - -.signup-container label, .signup-main h2 { - text-align: center; -} - - -.signup-button { - background-color:chartreuse; -} - -.signup-button:hover { - cursor: pointer; - background-color: rgb(88, 173, 2) -} - - -.not-found { - width: 100%; - margin: 0; - height: 100%; - background-color: #1d3041; - font-family: "Open Sans - Semibold", sans-serif; - color: #fff; - - } - .bl_page404 h1 { - text-align: center; - margin-top: 1%; - margin-bottom: 25px; - font-size: 30px; - font-weight: 400; - text-transform: uppercase; - } - .bl_page404 p { - display: block; - margin: 25px auto; - max-width: 776px; - text-align: center; - color: #bcecf2; - font-family: "Open Sans", sans-serif; - font-size: 16px; - font-weight: 400; - line-height: 24px; - } - .bl_page404__wrapper { - position: relative; - width: 100%; - margin: 10px auto 10px; - max-width: 440px; - min-height: 410px; - - } - .bl_page404__img { - width: 100%; - } - .bl_page404__link { - display: block; - margin: 0 auto; - width: 260px; - height: 64px; - box-shadow: 0 5px 0 #9c1007, inset 0 0 18px rgba(253, 60, 0, 0.75); - background-color: #f95801; - color: #fff; - font-family: "Open Sans", sans-serif; - font-size: 24px; - font-weight: 700; - line-height: 64px; - text-transform: uppercase; - text-decoration: none; - border-radius: 30px; - text-align: center; - } - .bl_page404__link:hover, - .bl_page404__link:focus { - background-color: #ff7400; - } - .bl_page404__el1 { - position: absolute; - top: 108px; - left: 102px; - opacity: 1; - animation: el1Move 800ms linear infinite; - width: 84px; - height: 106px; - background: url("https://github.com/BlackStar1991/Pictures-for-sharing-/blob/master/404/bigBoom/404-1.png?raw=true") - 50% 50% no-repeat; - z-index: 2; - } - .bl_page404__el2 { - position: absolute; - top: 92px; - left: 136px; - opacity: 1; - animation: el2Move 800ms linear infinite; - width: 184px; - height: 106px; - background: url("https://github.com/BlackStar1991/Pictures-for-sharing-/blob/master/404/bigBoom/404-2.png?raw=true") - 50% 50% no-repeat; - z-index: 2; - } - .bl_page404__el3 { - position: absolute; - top: 108px; - left: 180px; - opacity: 1; - animation: el3Move 800ms linear infinite; - width: 284px; - height: 106px; - background: url("https://github.com/BlackStar1991/Pictures-for-sharing-/blob/master/404/bigBoom/404-3.png?raw=true") - 50% 50% no-repeat; - z-index: 2; - } - @keyframes el1Move { - 0% { - top: 108px; - left: 102px; - opacity: 1; - } - 100% { - top: -10px; - left: 22px; - opacity: 0; - } - } - @keyframes el2Move { - 0% { - top: 92px; - left: 136px; - opacity: 1; - } - 100% { - top: -10px; - left: 108px; - opacity: 0; - } - } - @keyframes el3Move { - 0% { - top: 108px; - left: 180px; - opacity: 1; - } - 100% { - top: 28px; - left: 276px; - opacity: 0; - } - } - -.post-content{ - display: flex; - flex-direction: column; -} - -.post-separator{ - height: 1px; - width: 73vw; - background-color: rgba(128, 128, 128, 0.301); - margin-left: auto; - margin-right: auto; -} - -.posts-container { - width: 80vw; - min-height: fit-content; - background-color: white; - border-left: 1px solid rgba(128, 128, 128, 0.301); -} - -.posts-container h1 { - margin-left: 70px; - margin-top: 15px; - margin-right: 70px; - font-size: 30px; - font-weight: 500; - margin-bottom: 5px; -} - -.posts-container h2 { - margin: 30px 70px 0px 70px; - font-size: 20px; - font-weight: 300; - border-bottom: 1px solid gray; - color:#d1170a; - padding-bottom: 20px; -} - -.posts-container p { - width: 800px; - margin-left: 70px; - margin-right: 70px; - font-weight: 300; -} - -.ratings { - display: flex; - flex-direction: row; - margin-left: 70px; - gap: 10px; -} - -.ratings button { - padding: 5px; - border-radius: 5px; - border: 1px solid gray; -} - -.like { - background-color: rgb(119, 238, 119); -} - -.dislike { - background-color:rgb(241, 99, 99); -} - -.reply-textarea { - width: 300px; - margin-top: 20px; - margin-left: -130px; - margin-bottom: 10px; -} - -.submit-reply { - margin-left: -130px; -} - -.posts-container .name { - font-weight: 400; -} - -.user { - text-align: center; -} - -.user h1 { - text-align: center; - font-weight: 400; - border-bottom: 1px solid rgba(128, 128, 128, 0.124); - padding-bottom: 20px; -} - -.user-info { - text-align: center; - font-weight: 300px; - padding-bottom: 10px; -} - -.user .user-info span{ - font-weight: 600; -} - - -.user .email { - margin-left: 50px; -} - -.delete-form { - background-color: #c3c3c31c; - border-radius: 10px; - padding: 40px; -} - -.delete-form div { - margin: auto; - width: 400px; -} - -.delete-form label { - text-align: center; - margin-bottom: 10px; - font-size: 24px; - font-weight: 400; -} - -.delete-confirm { - padding: 5px 10px 5px 10px; - border-radius: 5px; - border: 1px solid gray; -} - -.delete { - margin-top: -30px; - padding: 5px 10px 5px 10px; - border-radius: 5px; - border: 1px solid gray; - color: white; - background-color: #db1709; - font-weight: 600; -} - -.reply-container { - display: flex; - flex-direction: column; -} - - - -.ratings { - margin-right: 70px; -} - -.reply-container span { - border-bottom: 1px solid rgba(128, 128, 128, 0.346); - margin: 0px 70px 0px 70px; -} - -.filters-create { - display: flex; - flex-direction: row; - justify-content: space-between; -} - -.create { - margin-top: 30px; - margin-right: 70px; -} - -.create input { - padding: 6px; - background-color: rgb(53, 85, 193); - border: 1px solid rgb(128, 128, 128); - border-radius: 3px; - color: rgb(255, 255, 255); -} - -.create-post-main { - height: 100vh; - display: flex; -} - -.create-post { - background-color: #99999928; - margin: auto; - border-radius: 3px; - border: 1px solid gray; - width: 500px; - height: 500px; -} - -.create-post h1 { - font-size: 30px; - text-align: center; -} - -.create-post div { - margin-left: 70px; -} - -.title input { - margin-top: 5px; - width: 300px; - height: 1.4rem; -} - -.content textarea { - width: 300px; - height: 5rem; - margin-top: 5px; -} - -.create-post-category { - display: flex; - flex-direction: row; - flex-wrap: wrap; - gap: 10px; - width: 300px; -} - -.submit-post { - - margin: 20px 0 0 70px; - text-align: center; - font-size: 1rem; - padding: 5px 10px 5px 10px; - background-color: chartreuse; - border: 1px solid gray; - -} - -.post-time{ - font-size: 16px; - color: rgba(26, 26, 26, 0.517); - margin-bottom: 15px; -} - -.time { - font-size: 16px; - color: rgba(26, 26, 26, 0.517); - -} - -.reply-container .name { - margin-top: 0px; -} - -.create:hover{ - cursor: pointer; -} - -.posts a, a:visited { - color: inherit; -} - -.posts a { - text-decoration: none; -} - -.post-reply { - padding-bottom: 10px; -} - -.like, .dislike , .reply-button { - transition: all 0.1s ease; -} - -.like:hover, .dislike:hover, .reply-button:hover { - cursor: pointer; - padding: 6px; -} - -.post-reply .name { - margin-top: 10px; -} - -.signup-error { - color: red; - text-align: center; -} - -.login-options img { - height: 30px; - width: 30px; - padding: 0px; - margin: 0px; -} - -.google-login, .github-login { - margin: auto; - height: auto; - display: flex; - align-items: center; - margin-top: 10px; -} - - -.login-options a { - text-decoration: none; -} - -.login-options { - margin-top: 7px; -} - -.github-login .img { - margin-right: 5px; - -} - diff --git a/example/ui/templates/createPost.html b/example/ui/templates/createPost.html deleted file mode 100644 index c0b22e5..0000000 --- a/example/ui/templates/createPost.html +++ /dev/null @@ -1,60 +0,0 @@ - - - -
- - -Sorry! The page you are looking for can not be found. Perhaps the page you requested was moved or deleted. It is also possible that you made a small typo when entering the address. Go to the main page. -
-{{.CurrentPost.Username}}
-{{.CurrentPost.Content}}
- -- Created: {{.CurrentPost.Created.Format "15:04 January 2, 2006"}} -
-