Browse Source

Implement post deletion

master
Dylan Baker 3 years ago
parent
commit
f74f6d1dc7
6 changed files with 74 additions and 27 deletions
  1. 32
    16
      src/fs.rs
  2. 2
    1
      src/main.rs
  3. 2
    10
      src/post.rs
  4. 9
    0
      src/routes.rs
  5. 1
    0
      templates/components.html
  6. 28
    0
      templates/layout.html

+ 32
- 16
src/fs.rs View File

@@ -1,26 +1,16 @@
1 1
 use async_std::fs::read_to_string;
2 2
 use std::env;
3 3
 use std::ffi::OsStr;
4
-use std::fs::read_dir;
5
-use std::io::{Error, ErrorKind};
4
+use std::fs::{read_dir, File};
5
+use std::io::{Error, ErrorKind, Write};
6 6
 use std::path::PathBuf;
7 7
 
8 8
 use crate::post::Post;
9 9
 
10
-pub async fn get_posts_directory_path() -> Result<PathBuf, Error> {
11
-    match env::var("POSTS_DIR") {
12
-        Ok(dir) => Ok(dir.into()),
13
-        Err(_) => Err(Error::new(
14
-            ErrorKind::Other,
15
-            "Posts directory environment variable not set",
16
-        )),
17
-    }
18
-}
19
-
20 10
 pub async fn get_all_posts() -> Result<Vec<Post>, Error> {
21 11
     let mut posts: Vec<Post> = vec![];
22 12
 
23
-    for file in post_directory_contents().await? {
13
+    for file in post_directory_contents()? {
24 14
         let file = file?;
25 15
         if let Some("json") = file.path().extension().and_then(OsStr::to_str) {
26 16
             let contents = read_post_from_disk(&file.path()).await?;
@@ -33,7 +23,7 @@ pub async fn get_all_posts() -> Result<Vec<Post>, Error> {
33 23
 }
34 24
 
35 25
 pub async fn get_one_post(post_id: String) -> Result<Post, Error> {
36
-    let posts_dir = get_posts_directory_path().await?;
26
+    let posts_dir = get_posts_directory_path()?;
37 27
     let path = posts_dir.join(format!("{}.json", post_id));
38 28
     let contents = read_post_from_disk(&path).await?;
39 29
     let post = Post::from_str(&contents)?;
@@ -41,6 +31,13 @@ pub async fn get_one_post(post_id: String) -> Result<Post, Error> {
41 31
     Ok(post)
42 32
 }
43 33
 
34
+pub fn delete_post(post_id: String) -> Result<(), Error> {
35
+    let posts_dir = get_posts_directory_path()?;
36
+    let path = posts_dir.join(format!("{}.json", post_id));
37
+    std::fs::remove_file(path)?;
38
+    Ok(())
39
+}
40
+
44 41
 async fn read_post_from_disk(path: &PathBuf) -> Result<String, Error> {
45 42
     match read_to_string(&path).await {
46 43
         Ok(c) => Ok(c),
@@ -54,8 +51,17 @@ async fn read_post_from_disk(path: &PathBuf) -> Result<String, Error> {
54 51
     }
55 52
 }
56 53
 
57
-async fn post_directory_contents() -> Result<std::fs::ReadDir, Error> {
58
-    let path = get_posts_directory_path().await?;
54
+pub fn write_post_to_disk(post: &Post) -> Result<(), Error> {
55
+    let mut path: PathBuf = get_posts_directory_path()?;
56
+    let filename = format!("{}.json", post.id);
57
+    path = path.join(&filename);
58
+    let mut file = File::create(&path)?;
59
+    file.write_all(serde_json::to_string(&post)?.as_bytes())?;
60
+    Ok(())
61
+}
62
+
63
+fn post_directory_contents() -> Result<std::fs::ReadDir, Error> {
64
+    let path = get_posts_directory_path()?;
59 65
 
60 66
     match read_dir(path) {
61 67
         Ok(f) => Ok(f),
@@ -65,3 +71,13 @@ async fn post_directory_contents() -> Result<std::fs::ReadDir, Error> {
65 71
         )),
66 72
     }
67 73
 }
74
+
75
+fn get_posts_directory_path() -> Result<PathBuf, Error> {
76
+    match env::var("POSTS_DIR") {
77
+        Ok(dir) => Ok(dir.into()),
78
+        Err(_) => Err(Error::new(
79
+            ErrorKind::Other,
80
+            "Posts directory environment variable not set",
81
+        )),
82
+    }
83
+}

+ 2
- 1
src/main.rs View File

@@ -22,7 +22,8 @@ async fn main() -> std::io::Result<()> {
22 22
         .post(routes::create_post);
23 23
     app.at("/posts/:id")
24 24
         .get(routes::single_post)
25
-        .post(routes::update_post);
25
+        .post(routes::update_post)
26
+        .delete(routes::delete_post);
26 27
     app.at("/posts/:id/edit")
27 28
         .with(middleware::require_auth)
28 29
         .get(routes::edit_post);

+ 2
- 10
src/post.rs View File

@@ -1,9 +1,6 @@
1 1
 use serde::{Deserialize, Serialize};
2 2
 use serde_json;
3
-use std::fs::File;
4
-use std::io::prelude::*;
5 3
 use std::io::{Error, ErrorKind};
6
-use std::path::PathBuf;
7 4
 
8 5
 use crate::fs;
9 6
 
@@ -16,13 +13,8 @@ pub struct Post {
16 13
 }
17 14
 
18 15
 impl Post {
19
-    pub async fn save(&mut self) -> std::io::Result<()> {
20
-        let mut path: PathBuf = fs::get_posts_directory_path().await?;
21
-        let filename = format!("{}.json", self.id);
22
-        path = path.join(&filename);
23
-        let mut file = File::create(&path)?;
24
-        file.write_all(serde_json::to_string(&self)?.as_bytes())?;
25
-        Ok(())
16
+    pub async fn save(&mut self) -> Result<(), Error> {
17
+        fs::write_post_to_disk(self)
26 18
     }
27 19
 
28 20
     pub fn from_str(blob: &str) -> Result<Post, Error> {

+ 9
- 0
src/routes.rs View File

@@ -60,6 +60,15 @@ pub async fn update_post(mut req: Request<()>) -> Result {
60 60
     Ok(Redirect::new(format!("/posts/{}", post.id)).into())
61 61
 }
62 62
 
63
+pub async fn delete_post(req: Request<()>) -> Result {
64
+    let id: String = req.param("id")?;
65
+    fs::delete_post(id)?;
66
+    let mut res = Response::new(tide::StatusCode::Ok);
67
+    res.set_body("{\"success\": \"true\"}");
68
+    res.set_content_type(mime::JSON);
69
+    Ok(res)
70
+}
71
+
63 72
 pub async fn login_page(mut req: Request<()>) -> Result {
64 73
     let mut context = Context::new();
65 74
     let logged_in: bool = req.session().get("logged_in").unwrap_or(false);

+ 1
- 0
templates/components.html View File

@@ -6,6 +6,7 @@
6 6
       <a class="post__link" href="/posts/{{ post.id }}">view</a>
7 7
       {% endif %} {% if logged_in %}
8 8
       <a class="post__link" href="/posts/{{ post.id }}/edit">edit</a>
9
+      <span class="post__link danger" data-delete="{{ post.id }}">delete</span>
9 10
       {% endif %}
10 11
     </div>
11 12
     <span class="post__title">

+ 28
- 0
templates/layout.html View File

@@ -32,6 +32,10 @@
32 32
         padding: 0.15em 0.3em;
33 33
       }
34 34
 
35
+      .danger {
36
+        color: #942626;
37
+      }
38
+
35 39
       .container {
36 40
         margin: auto;
37 41
         max-width: 800px;
@@ -85,8 +89,14 @@
85 89
       }
86 90
 
87 91
       .post__link {
92
+        cursor: pointer;
88 93
         font-style: normal;
89 94
         font-weight: normal;
95
+        text-decoration: underline;
96
+      }
97
+
98
+      .post__link:hover {
99
+        text-decoration: none;
90 100
       }
91 101
 
92 102
       .post__body {
@@ -125,5 +135,23 @@
125 135
       </header>
126 136
       {% block content %} {% endblock %}
127 137
     </div>
138
+    <script>
139
+      document.querySelectorAll('[data-delete]').forEach((el) => {
140
+        el.addEventListener('click', (e) => {
141
+          if (confirm('Are you sure? This cannot be undone.')) {
142
+            let postId = e.target.dataset.delete;
143
+            fetch(`/posts/${postId}`, {
144
+              method: 'DELETE',
145
+            }).then((r) => {
146
+              r.json().then((data) => {
147
+                if (data.success) {
148
+                  window.location = '/';
149
+                }
150
+              });
151
+            });
152
+          }
153
+        });
154
+      });
155
+    </script>
128 156
   </body>
129 157
 </html>

Loading…
Cancel
Save