func getApiPostAuthorsHandler(w http.ResponseWriter, r *http.Request, params map[string]string) { userName := authentication.GetUserName(r) if userName != "" { post_id := params["id"] postId, err := strconv.ParseInt(post_id, 10, 64) if err != nil || postId < 1 { http.Error(w, err.Error(), http.StatusInternalServerError) return } var authors []structure.User authors, err = database.RetrieveAuthors(postId) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } json, err := json.Marshal(usersToJson(authors)) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") w.Write(json) return } else { http.Error(w, "Not logged in!", http.StatusInternalServerError) return } }
func getApiUserRemoveHandler(w http.ResponseWriter, r *http.Request, params map[string]string) { userName := authentication.GetUserName(r) if userName != "" { userRole, err := getUserRole(userName) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } log.Print("Role is " + strconv.Itoa(userRole)) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } else if userRole != 4 && userRole != 1 { // Only owner and admin can do it http.Error(w, "You don't have permission to access this data.", http.StatusForbidden) return } id, _ := strconv.ParseInt(params["id"], 10, 64) err = database.DeleteUserById(id) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } w.Header().Set("Content-Type", "text/html") w.Write([]byte("User Removed")) return } else { http.Error(w, "Not logged in!", http.StatusInternalServerError) return } }
//TODO: return list of users func getApiUsersHandler(w http.ResponseWriter, r *http.Request, params map[string]string) { userName := authentication.GetUserName(r) if userName != "" { userRole, err := getUserRole(userName) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } log.Print("Role is " + strconv.Itoa(userRole)) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } else if userRole != 4 && userRole != 1 { // Only owner and admin can do it http.Error(w, "You don't have permission to access this data.", http.StatusForbidden) return } users, err := database.RetrieveUsers() if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } usersJson := usersToJson(users) json, err := json.Marshal(usersJson) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") w.Write(json) return } else { http.Error(w, "Not logged in!", http.StatusInternalServerError) return } }
// API function to delete an image by its filename. func deleteApiImageHandler(w http.ResponseWriter, r *http.Request, _ map[string]string) { userName := authentication.GetUserName(r) if userName != "" { // TODO: Check if the user has permissions to delete the image // Get the file name from the json data decoder := json.NewDecoder(r.Body) var json JsonImage err := decoder.Decode(&json) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } err = filepath.Walk(filenames.ImagesFilepath, func(filePath string, info os.FileInfo, err error) error { if !info.IsDir() && filepath.Base(filePath) == filepath.Base(json.Filename) { err := os.Remove(filePath) if err != nil { return err } } return nil }) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } w.WriteHeader(http.StatusOK) w.Write([]byte("Image deleted!")) return } else { http.Error(w, "Not logged in!", http.StatusInternalServerError) return } }
// API function to update blog settings func patchApiBlogHandler(w http.ResponseWriter, r *http.Request, _ map[string]string) { userName := authentication.GetUserName(r) if userName != "" { userId, err := getUserId(userName) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } decoder := json.NewDecoder(r.Body) var json JsonBlog err = decoder.Decode(&json) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } // Make sure postPerPage is over 0 if json.PostsPerPage < 1 { json.PostsPerPage = 1 } // Remove blog url in front of navigation urls for index, _ := range json.NavigationItems { if strings.HasPrefix(json.NavigationItems[index].Url, json.Url) { json.NavigationItems[index].Url = strings.Replace(json.NavigationItems[index].Url, json.Url, "", 1) // If we removed the blog url, there should be a / in front of the url if !strings.HasPrefix(json.NavigationItems[index].Url, "/") { json.NavigationItems[index].Url = "/" + json.NavigationItems[index].Url } } } // Retrieve old blog settings for comparison blog, err := database.RetrieveBlog() if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } tempBlog := structure.Blog{Url: []byte(configuration.Config.Url), Title: []byte(json.Title), Description: []byte(json.Description), Logo: []byte(json.Logo), Cover: []byte(json.Cover), AssetPath: []byte("/assets/"), PostCount: blog.PostCount, PostsPerPage: json.PostsPerPage, ActiveTheme: json.ActiveTheme, NavigationItems: json.NavigationItems} err = methods.UpdateBlog(&tempBlog, userId) // Check if active theme setting has been changed, if so, generate templates from new theme if tempBlog.ActiveTheme != blog.ActiveTheme { err = templates.Generate() if err != nil { // If there's an error while generating the new templates, the whole program must be stopped. log.Fatal("Fatal error: Template data couldn't be generated from theme files: " + err.Error()) http.Error(w, err.Error(), http.StatusInternalServerError) return } } if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } w.WriteHeader(http.StatusOK) w.Write([]byte("Blog settings updated!")) return } else { http.Error(w, "Not logged in!", http.StatusInternalServerError) return } }
// API function to upload images func apiUploadHandler(w http.ResponseWriter, r *http.Request, _ map[string]string) { userName := authentication.GetUserName(r) if userName != "" { // Create multipart reader reader, err := r.MultipartReader() if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } // Slice to hold all paths to the files allFilePaths := make([]string, 0) // Copy each part to destination. for { part, err := reader.NextPart() if err == io.EOF { break } // If part.FileName() is empty, skip this iteration. if part.FileName() == "" { continue } // Folder structure: year/month/randomname filePath := filepath.Join(filenames.ImagesFilepath, time.Now().Format("2006"), time.Now().Format("01")) if os.MkdirAll(filePath, 0777) != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } dst, err := os.Create(filepath.Join(filePath, strconv.FormatInt(time.Now().Unix(), 10)+"_"+uuid.Formatter(uuid.NewV4(), uuid.FormatHex)+filepath.Ext(part.FileName()))) defer dst.Close() if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } if _, err := io.Copy(dst, part); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } // Rewrite to file path on server filePath = strings.Replace(dst.Name(), filenames.ImagesFilepath, "/images", 1) // Make sure to always use "/" as path separator (to make a valid url that we can use on the blog) filePath = filepath.ToSlash(filePath) allFilePaths = append(allFilePaths, filePath) } json, err := json.Marshal(allFilePaths) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") w.Write(json) return } else { http.Error(w, "Not logged in!", http.StatusInternalServerError) return } }
// Function to serve files belonging to the admin interface. func adminFileHandler(w http.ResponseWriter, r *http.Request, params map[string]string) { userName := authentication.GetUserName(r) if userName != "" { // Get arguments (files) http.ServeFile(w, r, filepath.Join(filenames.AdminFilepath, params["filepath"])) return } else { http.NotFound(w, r) return } }
// API function to get all images by pages func apiImagesHandler(w http.ResponseWriter, r *http.Request, params map[string]string) { userName := authentication.GetUserName(r) if userName != "" { number := params["number"] page, err := strconv.Atoi(number) if err != nil || page < 1 { http.Error(w, "Not a valid api function!", http.StatusInternalServerError) return } images := make([]string, 0) // Walk all files in images folder err = filepath.Walk(filenames.ImagesFilepath, func(filePath string, info os.FileInfo, err error) error { if !info.IsDir() && (strings.EqualFold(filepath.Ext(filePath), ".jpg") || strings.EqualFold(filepath.Ext(filePath), ".jpeg") || strings.EqualFold(filepath.Ext(filePath), ".gif") || strings.EqualFold(filepath.Ext(filePath), ".png") || strings.EqualFold(filepath.Ext(filePath), ".svg")) { // Rewrite to file path on server filePath = strings.Replace(filePath, filenames.ImagesFilepath, "/images", 1) // Make sure to always use "/" as path separator (to make a valid url that we can use on the blog) filePath = filepath.ToSlash(filePath) // Prepend file to slice (thus reversing the order) images = append([]string{filePath}, images...) } return nil }) if len(images) == 0 { // Write empty json array w.Header().Set("Content-Type", "application/json") w.Write([]byte("[]")) return } imagesPerPage := 15 start := (page * imagesPerPage) - imagesPerPage end := page * imagesPerPage if start > (len(images) - 1) { // Write empty json array w.Header().Set("Content-Type", "application/json") w.Write([]byte("[]")) return } if end > len(images) { end = len(images) } json, err := json.Marshal(images[start:end]) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") w.Write(json) return } else { http.Error(w, "Not logged in!", http.StatusInternalServerError) return } }
// API function to update a post. func patchApiPostHandler(w http.ResponseWriter, r *http.Request, _ map[string]string) { userName := authentication.GetUserName(r) if userName != "" { userId, err := getUserId(userName) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } userRole, err := getUserRole(userName) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } // Update post decoder := json.NewDecoder(r.Body) var json JsonPost err = decoder.Decode(&json) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } var postSlug string // Get current slug of post post, err := database.RetrievePostById(json.Id) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } if post.Author.Id != userId && userRole != 4 { http.Error(w, "Not your post", http.StatusInternalServerError) return } if json.Slug != post.Slug { // Check if user has submitted a custom slug postSlug = slug.Generate(json.Slug, "posts") } else { postSlug = post.Slug } currentTime := time.Now() *post = structure.Post{Id: json.Id, Title: []byte(json.Title), Slug: postSlug, Markdown: []byte(json.Markdown), Html: conversion.SanitizeHtml([]byte(json.Html)), IsFeatured: json.IsFeatured, IsPage: json.IsPage, IsPublished: json.IsPublished, Image: []byte(json.Image), Date: ¤tTime, Tags: methods.GenerateTagsFromCommaString(json.Tags), Author: &structure.User{Id: userId}} err = methods.UpdatePost(post) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } w.WriteHeader(http.StatusOK) w.Write([]byte("Post updated!")) return } else { http.Error(w, "Not logged in!", http.StatusInternalServerError) return } }
// API function to get all posts by pages func apiPostsHandler(w http.ResponseWriter, r *http.Request, params map[string]string) { userName := authentication.GetUserName(r) if userName != "" { userId, err := getUserId(userName) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } userRole, err := getUserRole(userName) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } number := params["number"] page, err := strconv.Atoi(number) if err != nil || page < 1 { http.Error(w, err.Error(), http.StatusInternalServerError) return } postsPerPage := int64(15) var posts []structure.Post if userRole == 4 { allPosts, err := database.RetrievePostsForApi(postsPerPage, ((int64(page) - 1) * postsPerPage)) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } posts = allPosts } else { userPosts, err := database.RetrievePostsForApiByAuthor(userId, postsPerPage, ((int64(page) - 1) * postsPerPage)) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } posts = userPosts } json, err := json.Marshal(postsToJson(posts)) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") w.Write(json) return } else { http.Error(w, "Not logged in!", http.StatusInternalServerError) return } }
// Function to route the /admin/ url accordingly. (Is user logged in? Is at least one user registered?) func adminHandler(w http.ResponseWriter, r *http.Request, _ map[string]string) { if database.RetrieveUsersCount() == 0 { http.Redirect(w, r, "/admin/register/", 302) return } else { userName := authentication.GetUserName(r) if userName != "" { http.ServeFile(w, r, filepath.Join(filenames.AdminFilepath, "admin.html")) return } else { http.Redirect(w, r, "/admin/login/", 302) return } } }
// API function to delete a post by id. func deleteApiPostHandler(w http.ResponseWriter, r *http.Request, params map[string]string) { userName := authentication.GetUserName(r) if userName != "" { userId, err := getUserId(userName) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } userRole, err := getUserRole(userName) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } id := params["id"] // Delete post postId, err := strconv.ParseInt(id, 10, 64) if err != nil || postId < 1 { http.Error(w, err.Error(), http.StatusInternalServerError) return } post, err := database.RetrievePostById(postId) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } if post.Author.Id != userId && userRole != 4 { http.Error(w, "Not your post", http.StatusInternalServerError) return } err = methods.DeletePost(postId) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } w.WriteHeader(http.StatusOK) w.Write([]byte("Post deleted!")) return } else { http.Error(w, "Not logged in!", http.StatusInternalServerError) return } }
// API function to get user settings func getApiUserHandler(w http.ResponseWriter, r *http.Request, params map[string]string) { userName := authentication.GetUserName(r) if userName != "" { userId, err := getUserId(userName) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } id := params["id"] userIdToGet, err := strconv.ParseInt(id, 10, 64) if err != nil || userIdToGet < 1 { http.Error(w, err.Error(), http.StatusInternalServerError) return } else if userIdToGet != userId { // Make sure the authenticated user is only accessing his/her own data. TODO: Make sure the user is admin when multiple users have been introduced http.Error(w, "You don't have permission to access this data.", http.StatusForbidden) return } user, err := database.RetrieveUser(userIdToGet) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } userRole, err := getUserRole(userName) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } user.Role = userRole userJson := userToJson(user) json, err := json.Marshal(userJson) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") w.Write(json) return } else { http.Error(w, "Not logged in!", http.StatusInternalServerError) return } }
// API function to get a post by id func getApiPostHandler(w http.ResponseWriter, r *http.Request, params map[string]string) { userName := authentication.GetUserName(r) if userName != "" { userId, err := getUserId(userName) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } userRole, err := getUserRole(userName) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } id := params["id"] // Get post postId, err := strconv.ParseInt(id, 10, 64) if err != nil || postId < 1 { http.Error(w, err.Error(), http.StatusInternalServerError) return } post, err := database.RetrievePostById(postId) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } if post.Author.Id != userId && userRole != 4 { http.Error(w, "Not your post", http.StatusInternalServerError) return } json, err := json.Marshal(postToJson(post)) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") w.Write(json) return } else { http.Error(w, "Not logged in!", http.StatusInternalServerError) return } }
// API function to get blog settings func getApiBlogHandler(w http.ResponseWriter, r *http.Request, _ map[string]string) { userName := authentication.GetUserName(r) if userName != "" { // Read lock the global blog methods.Blog.RLock() defer methods.Blog.RUnlock() blogJson := blogToJson(methods.Blog) json, err := json.Marshal(blogJson) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") w.Write(json) return } else { http.Error(w, "Not logged in!", http.StatusInternalServerError) return } }
// API function to get the id of the currently authenticated user func getApiUserIdHandler(w http.ResponseWriter, r *http.Request, _ map[string]string) { userName := authentication.GetUserName(r) if userName != "" { userId, err := getUserId(userName) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } jsonUserId := JsonUserId{Id: userId} json, err := json.Marshal(jsonUserId) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") w.Write(json) return } else { http.Error(w, "Not logged in!", http.StatusInternalServerError) return } }
// API function to create a post func postApiPostHandler(w http.ResponseWriter, r *http.Request, _ map[string]string) { userName := authentication.GetUserName(r) if userName != "" { userId, err := getUserId(userName) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } // Create post decoder := json.NewDecoder(r.Body) var json JsonPost err = decoder.Decode(&json) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } var postSlug string if json.Slug != "" { // Ceck if user has submitted a custom slug postSlug = slug.Generate(json.Slug, "posts") } else { postSlug = slug.Generate(json.Title, "posts") } currentTime := time.Now() post := structure.Post{Title: []byte(json.Title), Slug: postSlug, Markdown: []byte(json.Markdown), Html: conversion.SanitizeHtml([]byte(json.Html)), IsFeatured: json.IsFeatured, IsPage: json.IsPage, IsPublished: json.IsPublished, Image: []byte(json.Image), Date: ¤tTime, Tags: methods.GenerateTagsFromCommaString(json.Tags), Author: &structure.User{Id: userId}} err = methods.SavePost(&post) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } w.WriteHeader(http.StatusOK) w.Write([]byte("Post created!")) return } else { http.Error(w, "Not logged in!", http.StatusInternalServerError) return } }
// API function to patch user settings func patchApiUserHandler(w http.ResponseWriter, r *http.Request, _ map[string]string) { userName := authentication.GetUserName(r) if userName != "" { userId, err := getUserId(userName) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } decoder := json.NewDecoder(r.Body) var json JsonUser err = decoder.Decode(&json) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } // Make sure user id is over 0 if json.Id < 1 { http.Error(w, "Wrong user id.", http.StatusInternalServerError) return } else if userId != json.Id { // Make sure the authenticated user is only changing his/her own data. TODO: Make sure the user is admin when multiple users have been introduced http.Error(w, "You don't have permission to change this data.", http.StatusInternalServerError) return } // Get old user data to compare tempUser, err := database.RetrieveUser(json.Id) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } // Make sure user email is provided if json.Email == "" { json.Email = string(tempUser.Email) } // Make sure user name is provided if json.Name == "" { json.Name = string(tempUser.Name) } // Make sure user slug is provided if json.Slug == "" { json.Slug = tempUser.Slug } // Check if new name is already taken if json.Name != string(tempUser.Name) { _, err = database.RetrieveUserByName([]byte(json.Name)) if err == nil { // The new user name is already taken. Assign the old name. // TODO: Return error that will be displayed in the admin interface. json.Name = string(tempUser.Name) } } // Check if new slug is already taken if json.Slug != tempUser.Slug { _, err = database.RetrieveUserBySlug(json.Slug) if err == nil { // The new user slug is already taken. Assign the old slug. // TODO: Return error that will be displayed in the admin interface. json.Slug = tempUser.Slug } } user := structure.User{Id: json.Id, Name: []byte(json.Name), Slug: json.Slug, Email: []byte(json.Email), Image: []byte(json.Image), Cover: []byte(json.Cover), Bio: []byte(json.Bio), Website: []byte(json.Website), Location: []byte(json.Location)} err = methods.UpdateUser(&user, userId) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } if json.Password != "" && (json.Password == json.PasswordRepeated) { // Update password if a new one was submitted encryptedPassword, err := authentication.EncryptPassword(json.Password) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } err = database.UpdateUserPassword(user.Id, encryptedPassword, time.Now(), json.Id) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } } // Check if the user name was changed. If so, update the session cookie to the new user name. if json.Name != string(tempUser.Name) { logInUser(json.Name, w) } w.WriteHeader(http.StatusOK) w.Write([]byte("User settings updated!")) return } else { http.Error(w, "Not logged in!", http.StatusInternalServerError) return } }
// API function to add and remove post authors func putApiPostAuthorsHandler(w http.ResponseWriter, r *http.Request, params map[string]string) { userName := authentication.GetUserName(r) if userName != "" { userId, err := getUserId(userName) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } userRole, err := getUserRole(userName) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } post_id := params["id"] postId, err := strconv.ParseInt(post_id, 10, 64) if err != nil || postId < 1 { http.Error(w, err.Error(), http.StatusInternalServerError) return } // Check the post for existence (there's no foreign key constraint) post, err := database.RetrievePostById(postId) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } // Allow this action only to the main author if post.Author.Id != userId && userRole != 4 { http.Error(w, "Not your post", http.StatusInternalServerError) return } decoder := json.NewDecoder(r.Body) var json JsonChangePostAuthors err = decoder.Decode(&json) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } delete_author_ids := json.Delete add_author_ids := json.Add var existing_authors []structure.User existing_authors, err = database.RetrieveAuthors(postId) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } // Delete users for _, author_id := range delete_author_ids { err = database.DeletePostAuthor(postId, author_id) // Don't even check for errors } // Filter out ones who is already listed as author for _, author_id := range add_author_ids { author_exists := false for _, author := range existing_authors { if author.Id == author_id { author_exists = true break } } if !author_exists { _, err = database.RetrieveUser(author_id) if err == nil { // Verify that this user exists in DB // and save him _ = database.InsertPostAuthor(int(postId), author_id) } } } w.WriteHeader(http.StatusOK) w.Write([]byte("Authors added!")) return } else { http.Error(w, "Not logged in!", http.StatusInternalServerError) return } }