You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

main.rs 3.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. use std::env;
  2. use std::fs::{read_dir, File};
  3. use std::io::prelude::*;
  4. use std::io::{Error, ErrorKind};
  5. use std::path::PathBuf;
  6. use async_std::fs::read_to_string;
  7. use chrono::prelude::Local;
  8. use dotenv;
  9. use serde::{Deserialize, Serialize};
  10. use serde_json;
  11. use tera::{Context, Tera};
  12. use tide::utils::After;
  13. use tide::{Body, Response, StatusCode};
  14. use uuid::Uuid;
  15. #[derive(Debug, Serialize, Deserialize)]
  16. struct Post {
  17. id: String,
  18. title: String,
  19. body: String,
  20. date: String,
  21. }
  22. impl Post {
  23. fn save(&mut self) -> std::io::Result<()> {
  24. let mut path: PathBuf = get_posts_directory()?;
  25. let filename = format!("{}.json", self.id);
  26. path = path.join(&filename);
  27. let mut file = match File::create(&path) {
  28. Err(why) => panic!("couldn't create {}: {}", self.id, why),
  29. Ok(file) => file,
  30. };
  31. match file.write_all(serde_json::to_string(&self)?.as_bytes()) {
  32. Err(why) => Err(Error::new(
  33. ErrorKind::Other,
  34. format!("couldn't write to {}.json: {}", self.id, why),
  35. )),
  36. Ok(_) => Ok(()),
  37. }
  38. }
  39. }
  40. fn get_posts_directory() -> Result<PathBuf, Error> {
  41. match env::var("POSTS_DIR") {
  42. Ok(dir) => Ok(dir.into()),
  43. Err(_) => Err(Error::new(
  44. ErrorKind::Other,
  45. "Posts directory environment variable not set",
  46. )),
  47. }
  48. }
  49. async fn read_all_posts() -> Result<Vec<Post>, Error> {
  50. let path = get_posts_directory()?;
  51. let mut posts: Vec<Post> = vec![];
  52. for file in read_dir(path)? {
  53. let file = file?;
  54. let contents = read_to_string(file.path()).await?;
  55. let post: Post = serde_json::from_str(&contents)?;
  56. posts.push(post);
  57. }
  58. Ok(posts)
  59. }
  60. #[async_std::main]
  61. async fn main() -> std::io::Result<()> {
  62. dotenv::dotenv().ok();
  63. tide::log::start();
  64. let mut app = tide::new();
  65. app.with(After(|mut res: Response| async {
  66. if let Some(err) = res.downcast_error::<async_std::io::Error>() {
  67. println!("{:?}", res);
  68. if let ErrorKind::NotFound = err.kind() {
  69. res.set_status(StatusCode::NotFound);
  70. }
  71. }
  72. Ok(res)
  73. }));
  74. app.with(After(|mut res: Response| async {
  75. res.set_content_type(tide::http::mime::HTML);
  76. Ok(res)
  77. }));
  78. app.at("/admin").get(|_| async {
  79. let tera = Tera::new("templates/**/*.html")?;
  80. let posts = read_all_posts().await?;
  81. let mut context = Context::new();
  82. context.insert("posts", &posts);
  83. let html = tera.render("admin/index.html", &context)?;
  84. Ok(Body::from_string(html))
  85. });
  86. app.at("/admin/posts")
  87. .post(|mut req: tide::Request<()>| async move {
  88. let mut post: Post = req.body_form().await?;
  89. post.id = Uuid::new_v4().to_string();
  90. post.date = Local::now().date().naive_local().to_string();
  91. post.save()?;
  92. Ok(tide::Redirect::new("/admin"))
  93. });
  94. app.listen("127.0.0.1:8080").await?;
  95. Ok(())
  96. }