Преглед изворни кода

Implement post deletion

master
Dylan Baker пре 3 година
родитељ
комит
f74f6d1dc7
6 измењених фајлова са 74 додато и 27 уклоњено
  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 Прегледај датотеку

1
 use async_std::fs::read_to_string;
1
 use async_std::fs::read_to_string;
2
 use std::env;
2
 use std::env;
3
 use std::ffi::OsStr;
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
 use std::path::PathBuf;
6
 use std::path::PathBuf;
7
 
7
 
8
 use crate::post::Post;
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
 pub async fn get_all_posts() -> Result<Vec<Post>, Error> {
10
 pub async fn get_all_posts() -> Result<Vec<Post>, Error> {
21
     let mut posts: Vec<Post> = vec![];
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
         let file = file?;
14
         let file = file?;
25
         if let Some("json") = file.path().extension().and_then(OsStr::to_str) {
15
         if let Some("json") = file.path().extension().and_then(OsStr::to_str) {
26
             let contents = read_post_from_disk(&file.path()).await?;
16
             let contents = read_post_from_disk(&file.path()).await?;
33
 }
23
 }
34
 
24
 
35
 pub async fn get_one_post(post_id: String) -> Result<Post, Error> {
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
     let path = posts_dir.join(format!("{}.json", post_id));
27
     let path = posts_dir.join(format!("{}.json", post_id));
38
     let contents = read_post_from_disk(&path).await?;
28
     let contents = read_post_from_disk(&path).await?;
39
     let post = Post::from_str(&contents)?;
29
     let post = Post::from_str(&contents)?;
41
     Ok(post)
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
 async fn read_post_from_disk(path: &PathBuf) -> Result<String, Error> {
41
 async fn read_post_from_disk(path: &PathBuf) -> Result<String, Error> {
45
     match read_to_string(&path).await {
42
     match read_to_string(&path).await {
46
         Ok(c) => Ok(c),
43
         Ok(c) => Ok(c),
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
     match read_dir(path) {
66
     match read_dir(path) {
61
         Ok(f) => Ok(f),
67
         Ok(f) => Ok(f),
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 Прегледај датотеку

22
         .post(routes::create_post);
22
         .post(routes::create_post);
23
     app.at("/posts/:id")
23
     app.at("/posts/:id")
24
         .get(routes::single_post)
24
         .get(routes::single_post)
25
-        .post(routes::update_post);
25
+        .post(routes::update_post)
26
+        .delete(routes::delete_post);
26
     app.at("/posts/:id/edit")
27
     app.at("/posts/:id/edit")
27
         .with(middleware::require_auth)
28
         .with(middleware::require_auth)
28
         .get(routes::edit_post);
29
         .get(routes::edit_post);

+ 2
- 10
src/post.rs Прегледај датотеку

1
 use serde::{Deserialize, Serialize};
1
 use serde::{Deserialize, Serialize};
2
 use serde_json;
2
 use serde_json;
3
-use std::fs::File;
4
-use std::io::prelude::*;
5
 use std::io::{Error, ErrorKind};
3
 use std::io::{Error, ErrorKind};
6
-use std::path::PathBuf;
7
 
4
 
8
 use crate::fs;
5
 use crate::fs;
9
 
6
 
16
 }
13
 }
17
 
14
 
18
 impl Post {
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
     pub fn from_str(blob: &str) -> Result<Post, Error> {
20
     pub fn from_str(blob: &str) -> Result<Post, Error> {

+ 9
- 0
src/routes.rs Прегледај датотеку

60
     Ok(Redirect::new(format!("/posts/{}", post.id)).into())
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
 pub async fn login_page(mut req: Request<()>) -> Result {
72
 pub async fn login_page(mut req: Request<()>) -> Result {
64
     let mut context = Context::new();
73
     let mut context = Context::new();
65
     let logged_in: bool = req.session().get("logged_in").unwrap_or(false);
74
     let logged_in: bool = req.session().get("logged_in").unwrap_or(false);

+ 1
- 0
templates/components.html Прегледај датотеку

6
       <a class="post__link" href="/posts/{{ post.id }}">view</a>
6
       <a class="post__link" href="/posts/{{ post.id }}">view</a>
7
       {% endif %} {% if logged_in %}
7
       {% endif %} {% if logged_in %}
8
       <a class="post__link" href="/posts/{{ post.id }}/edit">edit</a>
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
       {% endif %}
10
       {% endif %}
10
     </div>
11
     </div>
11
     <span class="post__title">
12
     <span class="post__title">

+ 28
- 0
templates/layout.html Прегледај датотеку

32
         padding: 0.15em 0.3em;
32
         padding: 0.15em 0.3em;
33
       }
33
       }
34
 
34
 
35
+      .danger {
36
+        color: #942626;
37
+      }
38
+
35
       .container {
39
       .container {
36
         margin: auto;
40
         margin: auto;
37
         max-width: 800px;
41
         max-width: 800px;
85
       }
89
       }
86
 
90
 
87
       .post__link {
91
       .post__link {
92
+        cursor: pointer;
88
         font-style: normal;
93
         font-style: normal;
89
         font-weight: normal;
94
         font-weight: normal;
95
+        text-decoration: underline;
96
+      }
97
+
98
+      .post__link:hover {
99
+        text-decoration: none;
90
       }
100
       }
91
 
101
 
92
       .post__body {
102
       .post__body {
125
       </header>
135
       </header>
126
       {% block content %} {% endblock %}
136
       {% block content %} {% endblock %}
127
     </div>
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
   </body>
156
   </body>
129
 </html>
157
 </html>

Loading…
Откажи
Сачувај