diff --git a/Api/JsonSerialization/VerifyJson.go b/Api/JsonSerialization/VerifyJson.go index 862e7c8..3a3ae78 100644 --- a/Api/JsonSerialization/VerifyJson.go +++ b/Api/JsonSerialization/VerifyJson.go @@ -55,6 +55,7 @@ func CanConvert(t reflect.Type, v reflect.Value) bool { f float64 srcInt bool ) + isPtr = t.Kind() == reflect.Ptr isStruct = t.Kind() == reflect.Struct isArray = t.Kind() == reflect.Array diff --git a/Api/Posts.go b/Api/Posts.go index 77f37a4..074a536 100644 --- a/Api/Posts.go +++ b/Api/Posts.go @@ -2,70 +2,16 @@ package Api import ( "encoding/json" - "io/ioutil" "log" "net/http" "net/url" "strconv" - "git.tovijaeschke.xyz/tovi/SuddenImpactRecords/Api/Auth" - "git.tovijaeschke.xyz/tovi/SuddenImpactRecords/Api/JsonSerialization" "git.tovijaeschke.xyz/tovi/SuddenImpactRecords/Database" "git.tovijaeschke.xyz/tovi/SuddenImpactRecords/Models" "git.tovijaeschke.xyz/tovi/SuddenImpactRecords/Util" ) -func getPosts(w http.ResponseWriter, r *http.Request) { - var ( - posts []Models.Post - returnJson []byte - values url.Values - page, pageSize int - search string - err error - ) - - values = r.URL.Query() - - page, err = strconv.Atoi(values.Get("page")) - if err != nil { - log.Println("Could not parse page url argument") - Util.JsonReturn(w, 500, "An error occured") - return - } - - pageSize, err = strconv.Atoi(values.Get("pageSize")) - if err != nil { - log.Println("Could not parse pageSize url argument") - Util.JsonReturn(w, 500, "An error occured") - return - } - - search = values.Get("search") - - posts, err = Database.GetPosts(page, pageSize, search) - if err != nil { - log.Printf("An error occured: %s\n", err.Error()) - Util.JsonReturn(w, 500, "An error occured") - return - } - - if len(posts) == 0 { - Util.JsonReturn(w, 404, "No more data") - return - } - - returnJson, err = json.MarshalIndent(posts, "", " ") - if err != nil { - Util.JsonReturn(w, 500, "An error occured") - return - } - - // Return updated json - w.WriteHeader(http.StatusOK) - w.Write(returnJson) -} - func getFrontPagePosts(w http.ResponseWriter, r *http.Request) { var ( posts []Models.Post @@ -91,71 +37,48 @@ func getFrontPagePosts(w http.ResponseWriter, r *http.Request) { w.Write(returnJson) } -func getPost(w http.ResponseWriter, r *http.Request) { +func getPostsPublic(w http.ResponseWriter, r *http.Request) { var ( - postData Models.Post - returnJson []byte - err error + posts []Models.Post + returnJson []byte + values url.Values + page, pageSize int + search string + err error ) - postData, err = Util.GetPostById(w, r) - if err != nil { - return - } + values = r.URL.Query() - returnJson, err = json.MarshalIndent(postData, "", " ") + page, err = strconv.Atoi(values.Get("page")) if err != nil { + log.Println("Could not parse page url argument") Util.JsonReturn(w, 500, "An error occured") return } - // Return updated json - w.WriteHeader(http.StatusOK) - w.Write(returnJson) -} - -func createPost(w http.ResponseWriter, r *http.Request) { - var ( - requestBody []byte - postData Models.Post - returnJson []byte - err error - ) - - _, err = Auth.CheckCookie(r) + pageSize, err = strconv.Atoi(values.Get("pageSize")) if err != nil { - w.WriteHeader(http.StatusUnauthorized) + log.Println("Could not parse pageSize url argument") + Util.JsonReturn(w, 500, "An error occured") return } - requestBody, err = ioutil.ReadAll(r.Body) + search = values.Get("search") + + posts, err = Database.GetPostsPublic(page, pageSize, search) if err != nil { - log.Printf("Error encountered reading POST body: %s\n", err.Error()) + log.Printf("An error occured: %s\n", err.Error()) Util.JsonReturn(w, 500, "An error occured") return } - postData, err = JsonSerialization.DeserializePost(requestBody, []string{ - "id", - "links", - "images", - "videos", - "audios", - }, false) - if err != nil { - log.Printf("Invalid data provided to posts API: %s\n", err.Error()) - Util.JsonReturn(w, 405, "Invalid data") + if len(posts) == 0 { + Util.JsonReturn(w, 404, "No more data") return } - err = Database.CreatePost(&postData) - if err != nil { - Util.JsonReturn(w, 405, "Invalid data") - } - - returnJson, err = json.MarshalIndent(postData, "", " ") + returnJson, err = json.MarshalIndent(posts, "", " ") if err != nil { - log.Printf("An error occured: %s\n", err.Error()) Util.JsonReturn(w, 500, "An error occured") return } @@ -165,52 +88,28 @@ func createPost(w http.ResponseWriter, r *http.Request) { w.Write(returnJson) } -func updatePost(w http.ResponseWriter, r *http.Request) { +func getPostPublic(w http.ResponseWriter, r *http.Request) { var ( - postData Models.Post - requestBody []byte - returnJson []byte - id string - err error + postData Models.Post + returnJson []byte + id string + err error ) - _, err = Auth.CheckCookie(r) - if err != nil { - w.WriteHeader(http.StatusUnauthorized) - return - } - id, err = Util.GetPostId(r) if err != nil { - log.Printf("Error encountered getting id\n") - Util.JsonReturn(w, 500, "An error occured") - return - } - - requestBody, err = ioutil.ReadAll(r.Body) - if err != nil { - log.Printf("Error encountered reading POST body: %s\n", err.Error()) Util.JsonReturn(w, 500, "An error occured") return } - postData, err = JsonSerialization.DeserializePost(requestBody, []string{}, true) - if err != nil { - log.Printf("Invalid data provided to posts API: %s\n", err.Error()) - Util.JsonReturn(w, 405, "Invalid data") - return - } - - postData, err = Database.UpdatePost(id, &postData) + postData, err = Database.GetPostByIdPublic(id) if err != nil { - log.Printf("An error occured: %s\n", err.Error()) - Util.JsonReturn(w, 500, "An error occured") + Util.JsonReturn(w, 404, "Not found") return } returnJson, err = json.MarshalIndent(postData, "", " ") if err != nil { - log.Printf("An error occured: %s\n", err.Error()) Util.JsonReturn(w, 500, "An error occured") return } @@ -219,31 +118,3 @@ func updatePost(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) w.Write(returnJson) } - -func deletePost(w http.ResponseWriter, r *http.Request) { - var ( - postData Models.Post - err error - ) - - _, err = Auth.CheckCookie(r) - if err != nil { - w.WriteHeader(http.StatusUnauthorized) - return - } - - postData, err = Util.GetPostById(w, r) - if err != nil { - return - } - - err = Database.DeletePost(&postData) - if err != nil { - log.Printf("An error occured: %s\n", err.Error()) - Util.JsonReturn(w, 500, "An error occured") - return - } - - // Return updated json - w.WriteHeader(http.StatusOK) -} diff --git a/Api/PostsAdmin.go b/Api/PostsAdmin.go new file mode 100644 index 0000000..68c3734 --- /dev/null +++ b/Api/PostsAdmin.go @@ -0,0 +1,283 @@ +package Api + +import ( + "encoding/json" + "io/ioutil" + "log" + "net/http" + "net/url" + "strconv" + "time" + + "git.tovijaeschke.xyz/tovi/SuddenImpactRecords/Api/Auth" + "git.tovijaeschke.xyz/tovi/SuddenImpactRecords/Api/JsonSerialization" + "git.tovijaeschke.xyz/tovi/SuddenImpactRecords/Database" + "git.tovijaeschke.xyz/tovi/SuddenImpactRecords/Models" + "git.tovijaeschke.xyz/tovi/SuddenImpactRecords/Util" +) + +func getPosts(w http.ResponseWriter, r *http.Request) { + var ( + posts []Models.Post + returnJson []byte + values url.Values + page, pageSize int + search string + err error + ) + + _, err = Auth.CheckCookie(r) + if err != nil { + w.WriteHeader(http.StatusUnauthorized) + return + } + + values = r.URL.Query() + + page, err = strconv.Atoi(values.Get("page")) + if err != nil { + log.Println("Could not parse page url argument") + Util.JsonReturn(w, 500, "An error occured") + return + } + + pageSize, err = strconv.Atoi(values.Get("pageSize")) + if err != nil { + log.Println("Could not parse pageSize url argument") + Util.JsonReturn(w, 500, "An error occured") + return + } + + search = values.Get("search") + + posts, err = Database.GetPosts(page, pageSize, search) + if err != nil { + log.Printf("An error occured: %s\n", err.Error()) + Util.JsonReturn(w, 500, "An error occured") + return + } + + if len(posts) == 0 { + Util.JsonReturn(w, 404, "No more data") + return + } + + returnJson, err = json.MarshalIndent(posts, "", " ") + if err != nil { + Util.JsonReturn(w, 500, "An error occured") + return + } + + // Return updated json + w.WriteHeader(http.StatusOK) + w.Write(returnJson) +} + +func getPost(w http.ResponseWriter, r *http.Request) { + var ( + postData Models.Post + returnJson []byte + err error + ) + + _, err = Auth.CheckCookie(r) + if err != nil { + w.WriteHeader(http.StatusUnauthorized) + return + } + + postData, err = Util.GetPostById(w, r) + if err != nil { + return + } + + returnJson, err = json.MarshalIndent(postData, "", " ") + if err != nil { + Util.JsonReturn(w, 500, "An error occured") + return + } + + // Return updated json + w.WriteHeader(http.StatusOK) + w.Write(returnJson) +} + +func createPost(w http.ResponseWriter, r *http.Request) { + var ( + requestBody []byte + postData Models.Post + returnJson []byte + err error + ) + + _, err = Auth.CheckCookie(r) + if err != nil { + w.WriteHeader(http.StatusUnauthorized) + return + } + + requestBody, err = ioutil.ReadAll(r.Body) + if err != nil { + log.Printf("Error encountered reading POST body: %s\n", err.Error()) + Util.JsonReturn(w, 500, "An error occured") + return + } + + postData, err = JsonSerialization.DeserializePost(requestBody, []string{ + "id", + "links", + "images", + "videos", + "audios", + }, false) + if err != nil { + log.Printf("Invalid data provided to posts API: %s\n", err.Error()) + Util.JsonReturn(w, 405, "Invalid data") + return + } + + err = Database.CreatePost(&postData) + if err != nil { + Util.JsonReturn(w, 405, "Invalid data") + } + + returnJson, err = json.MarshalIndent(postData, "", " ") + if err != nil { + log.Printf("An error occured: %s\n", err.Error()) + Util.JsonReturn(w, 500, "An error occured") + return + } + + // Return updated json + w.WriteHeader(http.StatusOK) + w.Write(returnJson) +} + +func updatePost(w http.ResponseWriter, r *http.Request) { + var ( + postData Models.Post + requestBody []byte + returnJson []byte + id string + err error + ) + + _, err = Auth.CheckCookie(r) + if err != nil { + w.WriteHeader(http.StatusUnauthorized) + return + } + + id, err = Util.GetPostId(r) + if err != nil { + log.Printf("Error encountered getting id\n") + Util.JsonReturn(w, 500, "An error occured") + return + } + + requestBody, err = ioutil.ReadAll(r.Body) + if err != nil { + log.Printf("Error encountered reading POST body: %s\n", err.Error()) + Util.JsonReturn(w, 500, "An error occured") + return + } + + postData, err = JsonSerialization.DeserializePost(requestBody, []string{}, true) + if err != nil { + log.Printf("Invalid data provided to posts API: %s\n", err.Error()) + Util.JsonReturn(w, 405, "Invalid data") + return + } + + postData, err = Database.UpdatePost(id, &postData) + if err != nil { + log.Printf("An error occured: %s\n", err.Error()) + Util.JsonReturn(w, 500, "An error occured") + return + } + + returnJson, err = json.MarshalIndent(postData, "", " ") + if err != nil { + log.Printf("An error occured: %s\n", err.Error()) + Util.JsonReturn(w, 500, "An error occured") + return + } + + // Return updated json + w.WriteHeader(http.StatusOK) + w.Write(returnJson) +} + +func publishPost(w http.ResponseWriter, r *http.Request) { + var ( + now time.Time = time.Now() + postData Models.Post + returnJson []byte + err error + ) + + _, err = Auth.CheckCookie(r) + if err != nil { + w.WriteHeader(http.StatusUnauthorized) + return + } + + postData, err = Util.GetPostById(w, r) + if err != nil { + log.Printf("Error encountered getting id\n") + Util.JsonReturn(w, 500, "An error occured") + return + } + + if postData.PublishedAt == nil { + postData.PublishedAt = &now + } else { + postData.PublishedAt = nil + } + + postData, err = Database.UpdatePost(postData.ID.String(), &postData) + if err != nil { + log.Printf("An error occured: %s\n", err.Error()) + Util.JsonReturn(w, 500, "An error occured") + return + } + + returnJson, err = json.MarshalIndent(postData, "", " ") + if err != nil { + log.Printf("An error occured: %s\n", err.Error()) + Util.JsonReturn(w, 500, "An error occured") + return + } + + // Return updated json + w.WriteHeader(http.StatusOK) + w.Write(returnJson) +} + +func deletePost(w http.ResponseWriter, r *http.Request) { + var ( + postData Models.Post + err error + ) + + _, err = Auth.CheckCookie(r) + if err != nil { + w.WriteHeader(http.StatusUnauthorized) + return + } + + postData, err = Util.GetPostById(w, r) + if err != nil { + return + } + + err = Database.DeletePost(&postData) + if err != nil { + log.Printf("An error occured: %s\n", err.Error()) + Util.JsonReturn(w, 500, "An error occured") + return + } + + // Return updated json + w.WriteHeader(http.StatusOK) +} diff --git a/Api/Routes.go b/Api/Routes.go index 316db2c..edee089 100644 --- a/Api/Routes.go +++ b/Api/Routes.go @@ -17,17 +17,21 @@ func InitApiEndpoints(router *mux.Router) { api = router.PathPrefix("/api/v1/").Subrouter() + api.HandleFunc("/posts/front-page", getFrontPagePosts).Methods("GET") + api.HandleFunc("/post", getPostsPublic).Methods("GET") + api.HandleFunc("/post/{postID}", getPostPublic).Methods("GET") + // Define routes for posts api - api.HandleFunc("/post", getPosts).Methods("GET") - api.HandleFunc("/post", createPost).Methods("POST") - api.HandleFunc("/post/{postID}", getPost).Methods("GET") - api.HandleFunc("/post/{postID}", updatePost).Methods("PUT") - api.HandleFunc("/post/{postID}", deletePost).Methods("DELETE") + api.HandleFunc("/admin/post", getPosts).Methods("GET") + api.HandleFunc("/admin/post/{postID}", getPost).Methods("GET") - api.HandleFunc("/frontPagePosts", getFrontPagePosts).Methods("GET") + api.HandleFunc("/admin/post", createPost).Methods("POST") + api.HandleFunc("/admin/post/{postID}", updatePost).Methods("PUT") + api.HandleFunc("/admin/post/{postID}", deletePost).Methods("DELETE") + api.HandleFunc("/admin/post/{postID}/publish", publishPost).Methods("GET") - api.HandleFunc("/post/{postID}/image", createPostImage).Methods("POST") - api.HandleFunc("/post/{postID}/image/{imageID}", deletePostImage).Methods("DELETE") + api.HandleFunc("/admin/post/{postID}/image", createPostImage).Methods("POST") + api.HandleFunc("/admin/post/{postID}/image/{imageID}", deletePostImage).Methods("DELETE") // Define routes for users api api.HandleFunc("/admin/user", getUsers).Methods("GET") diff --git a/Api/Users.go b/Api/Users.go index e84a9e4..d12eda1 100644 --- a/Api/Users.go +++ b/Api/Users.go @@ -109,6 +109,12 @@ func createUser(w http.ResponseWriter, r *http.Request) { err error ) + _, err = Auth.CheckCookie(r) + if err != nil { + w.WriteHeader(http.StatusUnauthorized) + return + } + requestBody, err = ioutil.ReadAll(r.Body) if err != nil { log.Printf("Error encountered reading POST body: %s\n", err.Error()) diff --git a/Database/Posts.go b/Database/Posts.go index 2e5a360..453a585 100644 --- a/Database/Posts.go +++ b/Database/Posts.go @@ -9,7 +9,22 @@ import ( "gorm.io/gorm/clause" ) -func GetPosts(page, pageSize int, search string) ([]Models.Post, error) { +func GetFrontPagePosts() ([]Models.Post, error) { + var ( + posts []Models.Post + err error + ) + + err = DB.Where("front_page = ?", true). + Where("published_at IS NOT NULL"). + Order("order asc"). + Find(&posts). + Error + + return posts, err +} + +func GetPostsPublic(page, pageSize int, search string) ([]Models.Post, error) { var ( posts []Models.Post query *gorm.DB @@ -30,6 +45,7 @@ func GetPosts(page, pageSize int, search string) ([]Models.Post, error) { query = DB.Model(Models.Post{}). Preload(clause.Associations). + Where("published_at IS NOT NULL"). Offset(offset). Limit(pageSize). Order("created_at desc") @@ -47,65 +63,16 @@ func GetPosts(page, pageSize int, search string) ([]Models.Post, error) { return posts, err } -func GetFrontPagePosts() ([]Models.Post, error) { - var ( - posts []Models.Post - err error - ) - - err = DB.Where("front_page = ?", true). - Order("order asc"). - Find(&posts). - Error - - return posts, err -} - -func GetPostById(id string) (Models.Post, error) { +func GetPostByIdPublic(id string) (Models.Post, error) { var ( postData Models.Post err error ) err = DB.Preload(clause.Associations). + Where("published_at IS NOT NULL"). First(&postData, "id = ?", id). Error return postData, err } - -func CreatePost(postData *Models.Post) error { - return DB.Session(&gorm.Session{FullSaveAssociations: true}). - Create(postData). - Error -} - -func UpdatePost(id string, postData *Models.Post) (Models.Post, error) { - var ( - err error - ) - - DB.Model(postData). - Where("id = ?", id). - Association("PostLinks"). - Replace(postData.PostLinks) - - err = DB.Model(&Models.Post{}). - Select("*"). - Omit("id", "created_at", "updated_at", "deleted_at"). - Where("id = ?", id). - Updates(postData). - Error - - if err != nil { - return Models.Post{}, err - } - - return GetPostById(id) -} - -func DeletePost(postData *Models.Post) error { - return DB.Session(&gorm.Session{FullSaveAssociations: true}). - Delete(postData). - Error -} diff --git a/Database/PostsAdmin.go b/Database/PostsAdmin.go new file mode 100644 index 0000000..3715bc7 --- /dev/null +++ b/Database/PostsAdmin.go @@ -0,0 +1,97 @@ +package Database + +import ( + "fmt" + + "git.tovijaeschke.xyz/tovi/SuddenImpactRecords/Models" + + "gorm.io/gorm" + "gorm.io/gorm/clause" +) + +func GetPosts(page, pageSize int, search string) ([]Models.Post, error) { + var ( + posts []Models.Post + query *gorm.DB + offset int + err error + ) + + switch { + case pageSize > 100: + pageSize = 100 + case pageSize <= 0: + pageSize = 10 + } + + offset = page * pageSize + + search = fmt.Sprintf("%%%s%%", search) + + query = DB.Model(Models.Post{}). + Preload(clause.Associations). + Offset(offset). + Limit(pageSize). + Order("created_at desc") + + if search != "%%" { + query = query. + Where("title LIKE ?", search). + Or("content LIKE ?", search) + } + + err = query. + Find(&posts). + Error + + return posts, err +} + +func GetPostById(id string) (Models.Post, error) { + var ( + postData Models.Post + err error + ) + + err = DB.Preload(clause.Associations). + First(&postData, "id = ?", id). + Error + + return postData, err +} + +func CreatePost(postData *Models.Post) error { + return DB.Session(&gorm.Session{FullSaveAssociations: true}). + Create(postData). + Error +} + +func UpdatePost(id string, postData *Models.Post) (Models.Post, error) { + var ( + err error + ) + + DB.Model(postData). + Where("id = ?", id). + Association("PostLinks"). + Replace(postData.PostLinks) + + err = DB.Model(&Models.Post{}). + Select("*"). + Omit("id", "created_at", "updated_at", "deleted_at"). + Where("id = ?", id). + Updates(postData). + Error + + if err != nil { + return Models.Post{}, err + } + + return GetPostById(id) +} + +func DeletePost(postData *Models.Post) error { + return DB.Session(&gorm.Session{FullSaveAssociations: true}). + Delete(postData). + Error +} diff --git a/Frontend/vue/src/assets/css/admin.css b/Frontend/vue/src/assets/css/admin.css index 2e7812f..674c6e3 100644 --- a/Frontend/vue/src/assets/css/admin.css +++ b/Frontend/vue/src/assets/css/admin.css @@ -118,3 +118,18 @@ label[role=alert] { background-color: #e9ecef; opacity: 1; } + +.editor-form .input-group { + display: inline-block; + width: unset; + padding-right: 0.3rem; +} + +.ProseMirror { + outline: 0; + height: 100%; +} + +.ProseMirror p:last-child { + margin-bottom: 0; +} diff --git a/Frontend/vue/src/components/admin/components/editor/AdminEditor.vue b/Frontend/vue/src/components/admin/components/editor/AdminEditor.vue new file mode 100644 index 0000000..e4807b7 --- /dev/null +++ b/Frontend/vue/src/components/admin/components/editor/AdminEditor.vue @@ -0,0 +1,204 @@ + + + diff --git a/Frontend/vue/src/components/admin/components/list/AdminListHeader.vue b/Frontend/vue/src/components/admin/components/list/AdminListHeader.vue index a979803..5553222 100644 --- a/Frontend/vue/src/components/admin/components/list/AdminListHeader.vue +++ b/Frontend/vue/src/components/admin/components/list/AdminListHeader.vue @@ -18,7 +18,7 @@ type="button" @click="searchFunction" > - Search + diff --git a/Frontend/vue/src/components/admin/views/posts/AdminPostsForm.vue b/Frontend/vue/src/components/admin/views/posts/AdminPostsForm.vue index 4be8c8f..13eb3e7 100644 --- a/Frontend/vue/src/components/admin/views/posts/AdminPostsForm.vue +++ b/Frontend/vue/src/components/admin/views/posts/AdminPostsForm.vue @@ -21,7 +21,7 @@

Update Post

-
+
@@ -55,9 +55,18 @@
+
+
+ + +
+
+
Created At
+
Updated At
+ +
+
+ + +
+
+
- +
@@ -98,6 +123,7 @@ diff --git a/Frontend/vue/src/components/admin/views/posts/AdminPostsList.vue b/Frontend/vue/src/components/admin/views/posts/AdminPostsList.vue index f4e1843..4293223 100644 --- a/Frontend/vue/src/components/admin/views/posts/AdminPostsList.vue +++ b/Frontend/vue/src/components/admin/views/posts/AdminPostsList.vue @@ -93,7 +93,7 @@ export default { async getInitialPosts () { try { const response = await this.axios.get( - `/post?page=${this.page}&pageSize=${this.pageSize}&search=${this.search}` + `/admin/post?page=${this.page}&pageSize=${this.pageSize}&search=${this.search}` ) if (response.status === 200) { diff --git a/Frontend/vue/src/router/index.js b/Frontend/vue/src/router/index.js index 71c42e8..a4e816d 100644 --- a/Frontend/vue/src/router/index.js +++ b/Frontend/vue/src/router/index.js @@ -77,7 +77,7 @@ const router = createRouter({ }); router.beforeEach((to, from, next) => { - const user = admin.getters.getUser; + const user = admin.getUser; if ((to.name == 'AdminLogin' || to.name == 'AdminSignup') && user !== null && !to.params.unauthorized) { next({ name: 'AdminUsersList' }); diff --git a/Frontend/vue/src/utils/http/index.js b/Frontend/vue/src/utils/http/index.js index f558495..69fc136 100644 --- a/Frontend/vue/src/utils/http/index.js +++ b/Frontend/vue/src/utils/http/index.js @@ -4,10 +4,11 @@ import router from '@/router' import admin from '@/store/admin/index.js' const instance = axios.create({ - baseURL: "http://localhost:8080/api/v1/", - headers: { - "Content-Type": "application/json", - }, + baseURL: "http://localhost:8080/api/v1/", + headers: { + "Content-Type": "application/json", + }, + withCredentials: true, }); instance.interceptors.response.use(