feat: refactor a lot
This commit is contained in:
parent
fe49f97e4e
commit
f8a8bf1148
16
Cargo.lock
generated
16
Cargo.lock
generated
@ -930,6 +930,20 @@ name = "itoa"
|
|||||||
version = "0.4.3"
|
version = "0.4.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jsonwebtoken"
|
||||||
|
version = "6.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"ring 0.14.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"serde 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"serde_derive 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"untrusted 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kernel32-sys"
|
name = "kernel32-sys"
|
||||||
version = "0.2.2"
|
version = "0.2.2"
|
||||||
@ -1626,6 +1640,7 @@ dependencies = [
|
|||||||
"dotenv 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"dotenv 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)",
|
"futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"http 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)",
|
"http 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"jsonwebtoken 6.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"openssl 0.10.22 (registry+https://github.com/rust-lang/crates.io-index)",
|
"openssl 0.10.22 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -2428,6 +2443,7 @@ dependencies = [
|
|||||||
"checksum iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dbe6e417e7d0975db6512b90796e8ce223145ac4e33c377e4a42882a0e88bb08"
|
"checksum iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dbe6e417e7d0975db6512b90796e8ce223145ac4e33c377e4a42882a0e88bb08"
|
||||||
"checksum ipconfig 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1b7b78ac2cfe6655f29ccc510ce0500c8356c3756dbce759d0ecb8a3718f2ef3"
|
"checksum ipconfig 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1b7b78ac2cfe6655f29ccc510ce0500c8356c3756dbce759d0ecb8a3718f2ef3"
|
||||||
"checksum itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1306f3464951f30e30d12373d31c79fbd52d236e5e896fd92f96ec7babbbe60b"
|
"checksum itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1306f3464951f30e30d12373d31c79fbd52d236e5e896fd92f96ec7babbbe60b"
|
||||||
|
"checksum jsonwebtoken 6.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a81d1812d731546d2614737bee92aa071d37e9afa1409bc374da9e5e70e70b22"
|
||||||
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
|
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
|
||||||
"checksum language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a"
|
"checksum language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a"
|
||||||
"checksum lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bc5729f27f159ddd61f4df6228e827e86643d4d3e7c32183cb30a1c08f604a14"
|
"checksum lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bc5729f27f159ddd61f4df6228e827e86643d4d3e7c32183cb30a1c08f604a14"
|
||||||
|
@ -39,3 +39,4 @@ time = "0.1.42"
|
|||||||
rand = "0.6.5"
|
rand = "0.6.5"
|
||||||
rss = "1.7.0"
|
rss = "1.7.0"
|
||||||
actix = "0.8.3"
|
actix = "0.8.3"
|
||||||
|
jsonwebtoken = "6.0.1"
|
||||||
|
@ -0,0 +1 @@
|
|||||||
|
-- This file should undo anything in `up.sql`
|
@ -0,0 +1,2 @@
|
|||||||
|
-- Your SQL goes here
|
||||||
|
ALTER TABLE "public"."articles" ALTER COLUMN "publish_at" SET DEFAULT NOW();
|
28
src/main.rs
28
src/main.rs
@ -12,7 +12,7 @@ use actix_web::{
|
|||||||
middleware::{
|
middleware::{
|
||||||
cors::Cors,
|
cors::Cors,
|
||||||
identity::{CookieIdentityPolicy, Identity, IdentityService},
|
identity::{CookieIdentityPolicy, Identity, IdentityService},
|
||||||
Logger,
|
Logger, NormalizePath,
|
||||||
},
|
},
|
||||||
web, App, HttpServer,
|
web, App, HttpServer,
|
||||||
};
|
};
|
||||||
@ -32,12 +32,13 @@ mod models;
|
|||||||
mod pg_pool;
|
mod pg_pool;
|
||||||
mod routers;
|
mod routers;
|
||||||
mod schema;
|
mod schema;
|
||||||
|
mod utils;
|
||||||
mod view;
|
mod view;
|
||||||
|
|
||||||
embed_migrations!();
|
embed_migrations!();
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
static ref RANDOM_TOKEN_KEY: Vec<u8> = (0..32).map(|_| rand::random::<u8>()).collect();
|
static ref RANDOM_TOKEN_KEY: Vec<u8> = (0..32).map(|_| 0).collect();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
@ -62,33 +63,14 @@ fn main() {
|
|||||||
.data(FormConfig::default().limit(256_000))
|
.data(FormConfig::default().limit(256_000))
|
||||||
.wrap(Logger::default())
|
.wrap(Logger::default())
|
||||||
.wrap(Cors::default())
|
.wrap(Cors::default())
|
||||||
|
.wrap(NormalizePath)
|
||||||
.wrap(IdentityService::new(
|
.wrap(IdentityService::new(
|
||||||
CookieIdentityPolicy::new(&RANDOM_TOKEN_KEY)
|
CookieIdentityPolicy::new(&RANDOM_TOKEN_KEY)
|
||||||
.name("auth-cookie")
|
.name("auth-cookie")
|
||||||
.secure(false)
|
.secure(false)
|
||||||
.max_age_time(Duration::days(3)),
|
.max_age_time(Duration::days(3)),
|
||||||
))
|
))
|
||||||
.service(routers::article::homepage)
|
.service(routers::routes())
|
||||||
.service(routers::article::single_article)
|
|
||||||
.service(actix_files::Files::new(
|
|
||||||
"/statics",
|
|
||||||
"./templates/resources/",
|
|
||||||
))
|
|
||||||
.service(routers::admin::redirect_to_admin_panel)
|
|
||||||
.service(
|
|
||||||
web::scope("/admin/")
|
|
||||||
.service(routers::admin::admin_panel)
|
|
||||||
.service(routers::admin::admin_login)
|
|
||||||
.service(routers::admin::admin_authentication)
|
|
||||||
.service(routers::admin::article_creation)
|
|
||||||
.service(routers::admin::article_save)
|
|
||||||
.service(routers::admin::article_edit)
|
|
||||||
.service(routers::admin::article_deletion)
|
|
||||||
.service(routers::admin::change_password)
|
|
||||||
.service(routers::admin::change_setting),
|
|
||||||
)
|
|
||||||
.service(routers::rss::rss_page)
|
|
||||||
.service(routers::article::get_article_by_url)
|
|
||||||
})
|
})
|
||||||
.bind(("0.0.0.0", 8000))
|
.bind(("0.0.0.0", 8000))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
|
@ -20,52 +20,19 @@ pub struct Article {
|
|||||||
pub url: Option<String>,
|
pub url: Option<String>,
|
||||||
pub keywords: Vec<String>,
|
pub keywords: Vec<String>,
|
||||||
}
|
}
|
||||||
|
//
|
||||||
#[derive(Debug, Insertable, AsChangeset, Serialize, Deserialize)]
|
#[derive(Debug, Insertable, AsChangeset, Serialize, Deserialize)]
|
||||||
#[table_name = "articles"]
|
#[table_name = "articles"]
|
||||||
pub struct NewArticle {
|
pub struct NewArticle {
|
||||||
pub id: Option<i32>,
|
|
||||||
pub title: String,
|
pub title: String,
|
||||||
pub body: String,
|
pub body: String,
|
||||||
pub published: bool,
|
pub published: bool,
|
||||||
pub user_id: i32,
|
pub user_id: i32,
|
||||||
pub publish_at: NaiveDateTime,
|
pub publish_at: Option<NaiveDateTime>,
|
||||||
pub url: Option<String>,
|
pub url: Option<String>,
|
||||||
pub keywords: Vec<String>,
|
pub keywords: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod form {
|
|
||||||
use crate::models::article::NewArticle;
|
|
||||||
use chrono::NaiveDateTime;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
|
||||||
pub struct NewArticleForm {
|
|
||||||
pub id: Option<i32>,
|
|
||||||
pub title: String,
|
|
||||||
pub body: String,
|
|
||||||
pub published: bool,
|
|
||||||
pub user_id: i32,
|
|
||||||
pub publish_at: NaiveDateTime,
|
|
||||||
pub url: Option<String>,
|
|
||||||
pub keywords: String,
|
|
||||||
}
|
|
||||||
impl Into<NewArticle> for NewArticleForm {
|
|
||||||
fn into(self) -> NewArticle {
|
|
||||||
NewArticle {
|
|
||||||
id: self.id,
|
|
||||||
title: self.title,
|
|
||||||
body: self.body,
|
|
||||||
published: self.published,
|
|
||||||
user_id: self.user_id,
|
|
||||||
publish_at: self.publish_at,
|
|
||||||
url: self.url,
|
|
||||||
keywords: self.keywords.split(",").map(String::from).collect(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Article {
|
impl Article {
|
||||||
pub fn link(&self) -> String {
|
pub fn link(&self) -> String {
|
||||||
match self.url {
|
match self.url {
|
||||||
|
@ -3,8 +3,8 @@ use diesel::result::Error;
|
|||||||
|
|
||||||
pub mod article;
|
pub mod article;
|
||||||
pub mod setting;
|
pub mod setting;
|
||||||
|
pub mod token;
|
||||||
pub mod user;
|
pub mod user;
|
||||||
|
|
||||||
pub trait CRUD<CreatedModel, UpdateModel, PK> {
|
pub trait CRUD<CreatedModel, UpdateModel, PK> {
|
||||||
fn create(conn: &PgConnection, from: &CreatedModel) -> Result<Self, Error>
|
fn create(conn: &PgConnection, from: &CreatedModel) -> Result<Self, Error>
|
||||||
where
|
where
|
||||||
|
@ -21,8 +21,14 @@ pub struct SettingMap {
|
|||||||
pub url: String,
|
pub url: String,
|
||||||
pub analysis: String,
|
pub analysis: String,
|
||||||
}
|
}
|
||||||
|
#[derive(Queryable, Debug, Serialize, Deserialize, AsChangeset)]
|
||||||
|
#[table_name = "setting"]
|
||||||
|
pub struct UpdateSetting {
|
||||||
|
pub value: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
impl Setting {
|
impl Setting {
|
||||||
|
// TODO refactor this method
|
||||||
pub fn load(conn: &PgConnection) -> SettingMap {
|
pub fn load(conn: &PgConnection) -> SettingMap {
|
||||||
let settings = setting::table.load::<Setting>(conn).unwrap();
|
let settings = setting::table.load::<Setting>(conn).unwrap();
|
||||||
|
|
||||||
@ -51,7 +57,7 @@ impl Setting {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CRUD<(), Setting, String> for Setting {
|
impl CRUD<(), UpdateSetting, String> for Setting {
|
||||||
fn create(conn: &PgConnection, from: &()) -> Result<Self, Error> {
|
fn create(conn: &PgConnection, from: &()) -> Result<Self, Error> {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
@ -60,7 +66,7 @@ impl CRUD<(), Setting, String> for Setting {
|
|||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update(conn: &PgConnection, pk: String, value: &Setting) -> Result<Self, Error> {
|
fn update(conn: &PgConnection, pk: String, value: &UpdateSetting) -> Result<Self, Error> {
|
||||||
diesel::update(setting::table.find(&pk))
|
diesel::update(setting::table.find(&pk))
|
||||||
.set(value)
|
.set(value)
|
||||||
.get_result(conn)
|
.get_result(conn)
|
||||||
|
6
src/models/token.rs
Normal file
6
src/models/token.rs
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct Token {
|
||||||
|
pub token: String,
|
||||||
|
}
|
@ -11,6 +11,12 @@ use diesel::prelude::*;
|
|||||||
use diesel::{AsChangeset, Insertable, Queryable};
|
use diesel::{AsChangeset, Insertable, Queryable};
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
|
use crate::data::RubbleData;
|
||||||
|
use crate::utils::jwt::JWTClaims;
|
||||||
|
use actix_web::dev::Payload;
|
||||||
|
use actix_web::{error, FromRequest, HttpRequest};
|
||||||
|
use futures::IntoFuture;
|
||||||
|
|
||||||
#[derive(Queryable, Debug, Serialize, Insertable, AsChangeset)]
|
#[derive(Queryable, Debug, Serialize, Insertable, AsChangeset)]
|
||||||
#[table_name = "users"]
|
#[table_name = "users"]
|
||||||
pub struct User {
|
pub struct User {
|
||||||
@ -65,3 +71,41 @@ impl CRUD<(), User, i32> for User {
|
|||||||
users::table.filter(users::id.eq(pk)).first::<User>(conn)
|
users::table.filter(users::id.eq(pk)).first::<User>(conn)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl FromRequest for User {
|
||||||
|
type Error = actix_web::error::Error;
|
||||||
|
type Future = Result<Self, Self::Error>;
|
||||||
|
type Config = ();
|
||||||
|
|
||||||
|
fn from_request(req: &HttpRequest, payload: &mut Payload) -> Self::Future {
|
||||||
|
let tokens: Vec<&str> = req
|
||||||
|
.headers()
|
||||||
|
.get("Authorization")
|
||||||
|
.ok_or(error::ErrorUnauthorized("cannot find authorization header"))?
|
||||||
|
.to_str()
|
||||||
|
.map_err(|_| error::ErrorBadRequest("error on deserialize token"))?
|
||||||
|
.splitn(2, ' ')
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let user_id = JWTClaims::decode(tokens[1].into())
|
||||||
|
.map_err(|_| error::ErrorUnauthorized("invalid jwt token"))?;
|
||||||
|
let data = req
|
||||||
|
.app_data::<RubbleData>()
|
||||||
|
.ok_or(error::ErrorBadGateway("error on get rubble data"))?;
|
||||||
|
|
||||||
|
let result = User::find_by_username(&data.postgres(), &user_id)
|
||||||
|
.map_err(|_| error::ErrorUnauthorized("error on get user"))?;
|
||||||
|
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod input {
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize)]
|
||||||
|
pub struct LoginForm {
|
||||||
|
pub username: String,
|
||||||
|
pub password: String,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,224 +1,241 @@
|
|||||||
use actix_web::middleware::identity::Identity;
|
//use actix_web::middleware::identity::Identity;
|
||||||
use actix_web::web::Form;
|
//use actix_web::web::Form;
|
||||||
use actix_web::{get, post, web, Either, HttpResponse, Responder};
|
//use actix_web::{get, post, web, Either, HttpResponse, Responder};
|
||||||
use chrono::{NaiveDateTime, Utc};
|
//use chrono::{NaiveDateTime, Utc};
|
||||||
use serde::Deserialize;
|
//use serde::{Deserialize, Serialize};
|
||||||
use tera::{Context, Tera};
|
//use tera::{Context, Tera};
|
||||||
|
//
|
||||||
|
//use crate::data::RubbleData;
|
||||||
|
//use crate::models::article::{Article, NewArticle};
|
||||||
|
//use crate::models::setting::Setting;
|
||||||
|
//use crate::models::token::Token;
|
||||||
|
//use crate::models::user::User;
|
||||||
|
//use crate::models::CRUD;
|
||||||
|
//use crate::routers::RubbleResponder;
|
||||||
|
//
|
||||||
|
//use crate::utils::jwt::JWTClaims;
|
||||||
|
//
|
||||||
|
//#[derive(Deserialize, Serialize)]
|
||||||
|
//pub struct LoginForm {
|
||||||
|
// pub username: String,
|
||||||
|
// pub password: String,
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//#[derive(Deserialize)]
|
||||||
|
//pub struct NewPassword {
|
||||||
|
// password: String,
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//#[get("/admin")]
|
||||||
|
//pub fn redirect_to_admin_panel() -> RubbleResponder<()> {
|
||||||
|
// RubbleResponder::Redirect {
|
||||||
|
// to: "/admin/panel".into(),
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
|
||||||
use crate::data::RubbleData;
|
//
|
||||||
use crate::models::article::{Article, NewArticle};
|
//#[get("/login")]
|
||||||
use crate::models::setting::Setting;
|
//pub fn admin_login(id: Identity, data: web::Data<RubbleData>) -> RubbleResponder<()> {
|
||||||
use crate::models::user::User;
|
// match id.identity() {
|
||||||
use crate::models::CRUD;
|
// Some(_) => RubbleResponder::Redirect {
|
||||||
use crate::routers::RubbleResponder;
|
// to: "/admin/panel".into(),
|
||||||
|
// },
|
||||||
#[derive(Deserialize)]
|
// None => RubbleResponder::Html(data.render("admin/login.html", &Context::new())),
|
||||||
pub struct LoginForm {
|
// }
|
||||||
pub username: String,
|
//}
|
||||||
pub password: String,
|
//
|
||||||
}
|
//#[post("/login")]
|
||||||
|
//pub fn admin_authentication(
|
||||||
#[derive(Deserialize)]
|
// id: Identity,
|
||||||
pub struct NewPassword {
|
// user: Form<LoginForm>,
|
||||||
password: String,
|
// data: web::Data<RubbleData>,
|
||||||
}
|
//) -> RubbleResponder<()> {
|
||||||
|
// let fetched_user = User::find_by_username(&data.postgres(), &user.username);
|
||||||
#[get("/admin")]
|
//
|
||||||
pub fn redirect_to_admin_panel() -> impl Responder {
|
// match fetched_user {
|
||||||
RubbleResponder::Redirect("/admin/panel".into())
|
// Ok(login_user) => {
|
||||||
}
|
// if login_user.authenticated(&user.password) {
|
||||||
|
// id.remember(login_user.username);
|
||||||
#[get("/panel")]
|
// info!("admin login");
|
||||||
pub fn admin_panel(id: Identity, data: web::Data<RubbleData>) -> impl Responder {
|
// RubbleResponder::Redirect {
|
||||||
if id.identity().is_none() {
|
// to: "/admin/panel".into(),
|
||||||
return RubbleResponder::Redirect("/admin/login".into());
|
// }
|
||||||
}
|
// } else {
|
||||||
|
// // TODO flash message or throw unauthorized
|
||||||
let articles = Article::read(&data.postgres());
|
// warn!("try logining admin with wrong password '{}'", user.password);
|
||||||
let settings = Setting::load(&data.postgres());
|
// RubbleResponder::Redirect {
|
||||||
|
// to: "/admin/login".into(),
|
||||||
let admin = User::find_by_username(&data.postgres(), &id.identity().unwrap())
|
// }
|
||||||
.expect("cannot found this user");
|
// }
|
||||||
|
// }
|
||||||
let mut context = Context::new();
|
// Err(_) => RubbleResponder::Redirect {
|
||||||
context.insert("setting", &settings);
|
// to: "/admin/login".into(),
|
||||||
context.insert("articles", &articles);
|
// },
|
||||||
context.insert("admin", &admin);
|
// }
|
||||||
|
//}
|
||||||
RubbleResponder::Html(data.render("admin/panel.html", &context))
|
//
|
||||||
}
|
//#[get("/article/new")]
|
||||||
|
//pub fn article_creation(id: Identity, data: web::Data<RubbleData>) -> RubbleResponder<()> {
|
||||||
#[get("/login")]
|
// if id.identity().is_none() {
|
||||||
pub fn admin_login(id: Identity, data: web::Data<RubbleData>) -> impl Responder {
|
// return RubbleResponder::Redirect {
|
||||||
match id.identity() {
|
// to: "/admin/login".into(),
|
||||||
Some(_) => RubbleResponder::Redirect("/admin/panel".into()),
|
// };
|
||||||
None => RubbleResponder::Html(data.render("admin/login.html", &Context::new())),
|
// }
|
||||||
}
|
//
|
||||||
}
|
// let admin = User::find_by_username(&data.postgres(), &id.identity().unwrap())
|
||||||
|
// .expect("cannot found this user");
|
||||||
#[post("/login")]
|
//
|
||||||
pub fn admin_authentication(
|
// let mut context = Context::new();
|
||||||
id: Identity,
|
//
|
||||||
user: Form<LoginForm>,
|
// let article = NewArticle {
|
||||||
data: web::Data<RubbleData>,
|
// id: None,
|
||||||
) -> impl Responder {
|
// title: String::new(),
|
||||||
let fetched_user = User::find_by_username(&data.postgres(), &user.username);
|
// body: String::new(),
|
||||||
|
// published: true,
|
||||||
match fetched_user {
|
// user_id: admin.id,
|
||||||
Ok(login_user) => {
|
// publish_at: NaiveDateTime::from_timestamp(Utc::now().timestamp(), 0),
|
||||||
if login_user.authenticated(&user.password) {
|
// url: None,
|
||||||
id.remember(login_user.username);
|
// keywords: vec![],
|
||||||
info!("admin login");
|
// };
|
||||||
RubbleResponder::Redirect("/admin/panel".into())
|
//
|
||||||
} else {
|
// context.insert("article", &article);
|
||||||
// TODO flash message or throw unauthorized
|
// RubbleResponder::Html(data.render("admin/article_add.html", &context))
|
||||||
warn!("try logining admin with wrong password '{}'", user.password);
|
//}
|
||||||
RubbleResponder::Redirect("/admin/login".into())
|
//
|
||||||
}
|
//#[get("/article/{article_id}")]
|
||||||
}
|
//pub fn article_edit(
|
||||||
Err(_) => RubbleResponder::Redirect("/admin/login".into()),
|
// id: Identity,
|
||||||
}
|
// article_id: web::Path<i32>,
|
||||||
}
|
// data: web::Data<RubbleData>,
|
||||||
|
//) -> RubbleResponder<()> {
|
||||||
#[get("/article/new")]
|
// if id.identity().is_none() {
|
||||||
pub fn article_creation(id: Identity, data: web::Data<RubbleData>) -> impl Responder {
|
// return RubbleResponder::Redirect {
|
||||||
if id.identity().is_none() {
|
// to: "/admin/login".into(),
|
||||||
return RubbleResponder::Redirect("/admin/login".into());
|
// };
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
let admin = User::find_by_username(&data.postgres(), &id.identity().unwrap())
|
// let admin = User::find_by_username(&data.postgres(), &id.identity().unwrap())
|
||||||
.expect("cannot found this user");
|
// .expect("cannot found this user");
|
||||||
|
//
|
||||||
let mut context = Context::new();
|
// let result = Article::get_by_pk(&data.postgres(), article_id.into_inner());
|
||||||
|
//
|
||||||
let article = NewArticle {
|
// match result {
|
||||||
id: None,
|
// Ok(article) => {
|
||||||
title: String::new(),
|
// let mut context = Context::new();
|
||||||
body: String::new(),
|
// context.insert("article", &article);
|
||||||
published: true,
|
// RubbleResponder::Html(data.render("admin/article_add.html", &context))
|
||||||
user_id: admin.id,
|
// }
|
||||||
publish_at: NaiveDateTime::from_timestamp(Utc::now().timestamp(), 0),
|
// Err(_) => RubbleResponder::Redirect {
|
||||||
url: None,
|
// to: "/admin/panel".into(),
|
||||||
keywords: vec![],
|
// },
|
||||||
};
|
// }
|
||||||
|
//}
|
||||||
context.insert("article", &article);
|
//
|
||||||
RubbleResponder::Html(data.render("admin/article_add.html", &context))
|
//#[post("/article")]
|
||||||
}
|
//pub fn article_save(
|
||||||
|
// id: Identity,
|
||||||
#[get("/article/{article_id}")]
|
// article: Form<crate::models::article::form::NewArticleForm>,
|
||||||
pub fn article_edit(
|
// data: web::Data<RubbleData>,
|
||||||
id: Identity,
|
//) -> RubbleResponder<()> {
|
||||||
article_id: web::Path<i32>,
|
// if id.identity().is_none() {
|
||||||
data: web::Data<RubbleData>,
|
// return RubbleResponder::Redirect {
|
||||||
) -> impl Responder {
|
// to: "/admin/login".into(),
|
||||||
if id.identity().is_none() {
|
// };
|
||||||
return RubbleResponder::Redirect("/admin/login".into());
|
// }
|
||||||
}
|
//
|
||||||
|
// let article_title = article.title.clone();
|
||||||
let admin = User::find_by_username(&data.postgres(), &id.identity().unwrap())
|
//
|
||||||
.expect("cannot found this user");
|
// let admin = User::find_by_username(&data.postgres(), &id.identity().unwrap())
|
||||||
|
// .expect("cannot found this user");
|
||||||
let result = Article::get_by_pk(&data.postgres(), article_id.into_inner());
|
// let res = if let Some(article_id) = article.id {
|
||||||
|
// info!("updating article #{} {}", article_id, article_title);
|
||||||
match result {
|
// Article::update(&data.postgres(), article_id, &article.into_inner().into())
|
||||||
Ok(article) => {
|
// } else {
|
||||||
let mut context = Context::new();
|
// info!("creating new article {}", article_title);
|
||||||
context.insert("article", &article);
|
// Article::create(&data.postgres(), &article.into_inner().into())
|
||||||
RubbleResponder::Html(data.render("admin/article_add.html", &context))
|
// };
|
||||||
}
|
//
|
||||||
Err(_) => RubbleResponder::Redirect("/admin/panel".into()),
|
// if res.is_err() {
|
||||||
}
|
// error!("error on updating/creating article {}", article_title);
|
||||||
}
|
// }
|
||||||
|
// RubbleResponder::Redirect {
|
||||||
#[post("/article")]
|
// to: "/admin/panel".into(),
|
||||||
pub fn article_save(
|
// }
|
||||||
id: Identity,
|
//}
|
||||||
article: Form<crate::models::article::form::NewArticleForm>,
|
//
|
||||||
data: web::Data<RubbleData>,
|
//#[post("/article/delete/{article_id}")]
|
||||||
) -> impl Responder {
|
//pub fn article_deletion(
|
||||||
if id.identity().is_none() {
|
// id: Identity,
|
||||||
return RubbleResponder::Redirect("/admin/login".into());
|
// article_id: web::Path<i32>,
|
||||||
}
|
// data: web::Data<RubbleData>,
|
||||||
|
//) -> RubbleResponder<()> {
|
||||||
let article_title = article.title.clone();
|
// if id.identity().is_none() {
|
||||||
|
// return RubbleResponder::Redirect {
|
||||||
let admin = User::find_by_username(&data.postgres(), &id.identity().unwrap())
|
// to: "/admin/login".into(),
|
||||||
.expect("cannot found this user");
|
// };
|
||||||
let res = if let Some(article_id) = article.id {
|
// }
|
||||||
info!("updating article #{} {}", article_id, article_title);
|
//
|
||||||
Article::update(&data.postgres(), article_id, &article.into_inner().into())
|
// let admin = User::find_by_username(&data.postgres(), &id.identity().unwrap())
|
||||||
} else {
|
// .expect("cannot found this user");
|
||||||
info!("creating new article {}", article_title);
|
//
|
||||||
Article::create(&data.postgres(), &article.into_inner().into())
|
// let i = article_id.into_inner();
|
||||||
};
|
// Article::delete(&data.postgres(), i);
|
||||||
|
// info!("deleting article {}", i);
|
||||||
if res.is_err() {
|
// RubbleResponder::Redirect {
|
||||||
error!("error on updating/creating article {}", article_title);
|
// to: "/admin/panel".into(),
|
||||||
}
|
// }
|
||||||
RubbleResponder::Redirect("/admin/panel".into())
|
//}
|
||||||
}
|
//
|
||||||
|
//#[post("/password")]
|
||||||
#[post("/article/delete/{article_id}")]
|
//pub fn change_password(
|
||||||
pub fn article_deletion(
|
// id: Identity,
|
||||||
id: Identity,
|
// password: web::Form<NewPassword>,
|
||||||
article_id: web::Path<i32>,
|
// data: web::Data<RubbleData>,
|
||||||
data: web::Data<RubbleData>,
|
//) -> RubbleResponder<()> {
|
||||||
) -> impl Responder {
|
// if id.identity().is_none() {
|
||||||
if id.identity().is_none() {
|
// return RubbleResponder::Redirect {
|
||||||
return RubbleResponder::Redirect("/admin/login".into());
|
// to: "/admin/login".into(),
|
||||||
}
|
// };
|
||||||
|
// }
|
||||||
let admin = User::find_by_username(&data.postgres(), &id.identity().unwrap())
|
//
|
||||||
.expect("cannot found this user");
|
// let mut admin = User::find_by_username(&data.postgres(), &id.identity().unwrap())
|
||||||
|
// .expect("cannot found this user");
|
||||||
let i = article_id.into_inner();
|
// admin.password = User::password_generate(&password.password).to_string();
|
||||||
Article::delete(&data.postgres(), i);
|
// User::update(&data.postgres(), admin.id, &admin);
|
||||||
info!("deleting article {}", i);
|
// id.forget();
|
||||||
RubbleResponder::Redirect("/admin/panel".into())
|
// info!("updating password");
|
||||||
}
|
// RubbleResponder::Redirect {
|
||||||
|
// to: "/admin/panel".into(),
|
||||||
#[post("/password")]
|
// }
|
||||||
pub fn change_password(
|
//}
|
||||||
id: Identity,
|
//
|
||||||
password: web::Form<NewPassword>,
|
//#[post("/setting")]
|
||||||
data: web::Data<RubbleData>,
|
//pub fn change_setting(
|
||||||
) -> impl Responder {
|
// id: Identity,
|
||||||
if id.identity().is_none() {
|
// setting: web::Form<Setting>,
|
||||||
return RubbleResponder::Redirect("/admin/login".into());
|
// data: web::Data<RubbleData>,
|
||||||
}
|
//) -> RubbleResponder<()> {
|
||||||
|
// if id.identity().is_none() {
|
||||||
let mut admin = User::find_by_username(&data.postgres(), &id.identity().unwrap())
|
// return RubbleResponder::Redirect {
|
||||||
.expect("cannot found this user");
|
// to: "/admin/login".into(),
|
||||||
admin.password = User::password_generate(&password.password).to_string();
|
// };
|
||||||
User::update(&data.postgres(), admin.id, &admin);
|
// }
|
||||||
id.forget();
|
//
|
||||||
info!("updating password");
|
// let mut admin = User::find_by_username(&data.postgres(), &id.identity().unwrap())
|
||||||
RubbleResponder::Redirect("/admin/panel".into())
|
// .expect("cannot found this user");
|
||||||
}
|
//
|
||||||
|
// Setting::update(&data.postgres(), setting.name.clone(), &setting);
|
||||||
#[post("/setting")]
|
// info!("updating setting {:?} to {:?}", setting.name, setting.value);
|
||||||
pub fn change_setting(
|
// RubbleResponder::Redirect {
|
||||||
id: Identity,
|
// to: "/admin/panel".into(),
|
||||||
setting: web::Form<Setting>,
|
// }
|
||||||
data: web::Data<RubbleData>,
|
//}
|
||||||
) -> impl Responder {
|
//
|
||||||
if id.identity().is_none() {
|
//#[cfg(test)]
|
||||||
return RubbleResponder::Redirect("/admin/login".into());
|
//mod test {
|
||||||
}
|
// #[test]
|
||||||
|
// fn test_normal() {
|
||||||
let mut admin = User::find_by_username(&data.postgres(), &id.identity().unwrap())
|
// assert_eq!(1, 1);
|
||||||
.expect("cannot found this user");
|
// }
|
||||||
|
//}
|
||||||
Setting::update(&data.postgres(), setting.name.clone(), &setting);
|
|
||||||
info!("updating setting {:?} to {:?}", setting.name, setting.value);
|
|
||||||
RubbleResponder::Redirect("/admin/panel".into())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
#[test]
|
|
||||||
fn test_normal() {
|
|
||||||
assert_eq!(1, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
56
src/routers/api/article.rs
Normal file
56
src/routers/api/article.rs
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
use crate::{
|
||||||
|
data::RubbleData,
|
||||||
|
models::{article::Article, user::User, CRUD},
|
||||||
|
routers::RubbleResponder,
|
||||||
|
};
|
||||||
|
use actix_web::{delete, get, post, put, web, Responder};
|
||||||
|
|
||||||
|
#[get("")]
|
||||||
|
pub fn get_all_article(user: User, data: web::Data<RubbleData>) -> impl Responder {
|
||||||
|
RubbleResponder::json(Article::read(&data.postgres()))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[get("/{id}")]
|
||||||
|
pub fn get_all_article_by_id(
|
||||||
|
user: User,
|
||||||
|
id: web::Path<i32>,
|
||||||
|
data: web::Data<RubbleData>,
|
||||||
|
) -> impl Responder {
|
||||||
|
Article::get_by_pk(&data.postgres(), *id)
|
||||||
|
.map(|data| RubbleResponder::json(data))
|
||||||
|
.map_err(|_| RubbleResponder::not_found())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[post("")]
|
||||||
|
pub fn crate_article(
|
||||||
|
user: User,
|
||||||
|
article: web::Json<crate::models::article::NewArticle>,
|
||||||
|
data: web::Data<RubbleData>,
|
||||||
|
) -> impl Responder {
|
||||||
|
Article::create(&data.postgres(), &article)
|
||||||
|
.map(RubbleResponder::json)
|
||||||
|
.map_err(|_| RubbleResponder::bad_request("something wrong when creating article"))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[put("/{id}")]
|
||||||
|
pub fn update_article_by_id(
|
||||||
|
user: User,
|
||||||
|
id: web::Path<i32>,
|
||||||
|
article: web::Json<crate::models::article::NewArticle>,
|
||||||
|
data: web::Data<RubbleData>,
|
||||||
|
) -> impl Responder {
|
||||||
|
Article::update(&data.postgres(), *id, &article)
|
||||||
|
.map(|data| RubbleResponder::json(data))
|
||||||
|
.map_err(|_| RubbleResponder::bad_request("something wrong when updating article"))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[delete("/{id}")]
|
||||||
|
pub fn delete_article_by_id(
|
||||||
|
user: User,
|
||||||
|
id: web::Path<i32>,
|
||||||
|
data: web::Data<RubbleData>,
|
||||||
|
) -> impl Responder {
|
||||||
|
Article::delete(&data.postgres(), *id)
|
||||||
|
.map(|_| RubbleResponder::json("Ok"))
|
||||||
|
.map_err(|_| RubbleResponder::bad_request("something wrong when deleting article"))
|
||||||
|
}
|
3
src/routers/api/mod.rs
Normal file
3
src/routers/api/mod.rs
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
pub mod article;
|
||||||
|
pub mod setting;
|
||||||
|
pub mod user;
|
28
src/routers/api/setting.rs
Normal file
28
src/routers/api/setting.rs
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
use crate::{
|
||||||
|
data::RubbleData,
|
||||||
|
models::{
|
||||||
|
setting::{Setting, UpdateSetting},
|
||||||
|
user::User,
|
||||||
|
CRUD,
|
||||||
|
},
|
||||||
|
routers::RubbleResponder,
|
||||||
|
};
|
||||||
|
use actix_web::{delete, get, post, put, web, Responder};
|
||||||
|
|
||||||
|
#[get("")]
|
||||||
|
pub fn get_settings(user: User, data: web::Data<RubbleData>) -> impl Responder {
|
||||||
|
RubbleResponder::json(Setting::load(&data.postgres()))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[put("/{key}")]
|
||||||
|
pub fn update_setting_by_key(
|
||||||
|
user: User,
|
||||||
|
key: web::Path<String>,
|
||||||
|
value: web::Json<UpdateSetting>,
|
||||||
|
data: web::Data<RubbleData>,
|
||||||
|
) -> impl Responder {
|
||||||
|
let string = (*key).clone();
|
||||||
|
Setting::update(&data.postgres(), string, &value)
|
||||||
|
.map(RubbleResponder::json)
|
||||||
|
.map_err(|_| RubbleResponder::bad_request("error on updating setting"))
|
||||||
|
}
|
58
src/routers/api/user.rs
Normal file
58
src/routers/api/user.rs
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
//! /authentications routes
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
data::RubbleData,
|
||||||
|
models::{
|
||||||
|
token::Token,
|
||||||
|
user::{input::LoginForm, User},
|
||||||
|
},
|
||||||
|
routers::RubbleResponder,
|
||||||
|
utils::jwt::JWTClaims,
|
||||||
|
};
|
||||||
|
use actix_web::{delete, get, post, put, web, Responder};
|
||||||
|
|
||||||
|
#[post("/token")]
|
||||||
|
pub fn admin_authentication(
|
||||||
|
user: web::Json<LoginForm>,
|
||||||
|
data: web::Data<RubbleData>,
|
||||||
|
) -> impl Responder {
|
||||||
|
let fetched_user = User::find_by_username(&data.postgres(), &user.username);
|
||||||
|
|
||||||
|
match fetched_user {
|
||||||
|
Ok(login_user) => {
|
||||||
|
if login_user.authenticated(&user.password) {
|
||||||
|
let string = JWTClaims::encode(&login_user);
|
||||||
|
|
||||||
|
RubbleResponder::json(Token { token: string })
|
||||||
|
} else {
|
||||||
|
RubbleResponder::unauthorized("invalid password")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(_) => RubbleResponder::unauthorized("invalid username"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[get("")]
|
||||||
|
pub fn get_all_users() -> impl Responder {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[post("")]
|
||||||
|
pub fn crate_user() -> impl Responder {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[put("/{id}")]
|
||||||
|
pub fn update_user_by_id() -> impl Responder {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[delete("/{id}")]
|
||||||
|
pub fn delete_user_by_id() -> impl Responder {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[put("/{id}/password")]
|
||||||
|
pub fn update_user_password() -> impl Responder {
|
||||||
|
unreachable!()
|
||||||
|
}
|
@ -6,10 +6,11 @@ use crate::pg_pool::Pool;
|
|||||||
use crate::routers::RubbleResponder;
|
use crate::routers::RubbleResponder;
|
||||||
use crate::view::article::ArticleView;
|
use crate::view::article::ArticleView;
|
||||||
use actix_web::{get, web, Either, HttpResponse, Responder};
|
use actix_web::{get, web, Either, HttpResponse, Responder};
|
||||||
|
use std::result::Result;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use tera::{Context, Tera};
|
use tera::{Context, Tera};
|
||||||
|
|
||||||
#[get("/")]
|
#[get("")]
|
||||||
pub fn homepage(data: web::Data<RubbleData>) -> impl Responder {
|
pub fn homepage(data: web::Data<RubbleData>) -> impl Responder {
|
||||||
let vec: Vec<Article> = Article::read(&data.postgres());
|
let vec: Vec<Article> = Article::read(&data.postgres());
|
||||||
let article_view: Vec<_> = vec
|
let article_view: Vec<_> = vec
|
||||||
@ -24,21 +25,21 @@ pub fn homepage(data: web::Data<RubbleData>) -> impl Responder {
|
|||||||
context.insert("setting", &settings);
|
context.insert("setting", &settings);
|
||||||
context.insert("articles", &article_view);
|
context.insert("articles", &article_view);
|
||||||
|
|
||||||
RubbleResponder::Html(data.render("homepage.html", &context))
|
RubbleResponder::html(data.render("homepage.html", &context))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/archives/{archives_id}")]
|
#[get("archives/{archives_id}")]
|
||||||
pub fn single_article(archives_id: web::Path<i32>, data: web::Data<RubbleData>) -> impl Responder {
|
pub fn single_article(archives_id: web::Path<i32>, data: web::Data<RubbleData>) -> impl Responder {
|
||||||
let article = Article::get_by_pk(&data.postgres(), archives_id.into_inner());
|
let article = Article::get_by_pk(&data.postgres(), archives_id.into_inner());
|
||||||
|
|
||||||
if let Err(e) = article {
|
if let Err(e) = article {
|
||||||
return RubbleResponder::NotFound;
|
return RubbleResponder::not_found();
|
||||||
}
|
}
|
||||||
let article1 = article.unwrap();
|
let article1 = article.unwrap();
|
||||||
|
|
||||||
if let Some(ref to) = article1.url {
|
if let Some(ref to) = article1.url {
|
||||||
if to.len() != 0 {
|
if to.len() != 0 {
|
||||||
return RubbleResponder::Redirect(format!("/{}", to));
|
return RubbleResponder::redirect(format!("/{}", to));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50,15 +51,15 @@ pub fn single_article(archives_id: web::Path<i32>, data: web::Data<RubbleData>)
|
|||||||
context.insert("setting", &settings);
|
context.insert("setting", &settings);
|
||||||
context.insert("article", &view);
|
context.insert("article", &view);
|
||||||
|
|
||||||
RubbleResponder::Html(data.render("archives.html", &context))
|
RubbleResponder::html(data.render("archives.html", &context))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/{url}")]
|
#[get("{url}")]
|
||||||
pub fn get_article_by_url(url: web::Path<String>, data: web::Data<RubbleData>) -> impl Responder {
|
pub fn get_article_by_url(url: web::Path<String>, data: web::Data<RubbleData>) -> impl Responder {
|
||||||
let article = Article::find_by_url(&data.postgres(), &url.into_inner());
|
let article = Article::find_by_url(&data.postgres(), &url.into_inner());
|
||||||
|
|
||||||
if let Err(e) = article {
|
if let Err(e) = article {
|
||||||
return RubbleResponder::NotFound;
|
return RubbleResponder::not_found();
|
||||||
}
|
}
|
||||||
let article1 = article.unwrap();
|
let article1 = article.unwrap();
|
||||||
|
|
||||||
@ -70,5 +71,5 @@ pub fn get_article_by_url(url: web::Path<String>, data: web::Data<RubbleData>) -
|
|||||||
context.insert("setting", &settings);
|
context.insert("setting", &settings);
|
||||||
context.insert("article", &view);
|
context.insert("article", &view);
|
||||||
|
|
||||||
RubbleResponder::Html(data.render("archives.html", &context))
|
RubbleResponder::html(data.render("archives.html", &context))
|
||||||
}
|
}
|
||||||
|
@ -1,35 +1,97 @@
|
|||||||
use actix_web::{HttpRequest, HttpResponse, Responder};
|
use actix_web::{HttpRequest, HttpResponse, Responder};
|
||||||
|
|
||||||
pub mod admin;
|
use actix_web::{error::Error, web, Scope};
|
||||||
pub mod article;
|
|
||||||
use actix_web::error::Error;
|
|
||||||
use futures::future::{err, ok, FutureResult};
|
use futures::future::{err, ok, FutureResult};
|
||||||
|
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
||||||
|
|
||||||
|
pub mod admin;
|
||||||
|
pub mod api;
|
||||||
|
pub mod article;
|
||||||
pub mod rss;
|
pub mod rss;
|
||||||
|
|
||||||
pub enum RubbleResponder {
|
#[derive(Deserialize, Serialize)]
|
||||||
Html(String),
|
pub struct JsonResponse<T> {
|
||||||
Redirect(String),
|
data: T,
|
||||||
NotFound,
|
|
||||||
RedirectPermanently(String),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Responder for RubbleResponder {
|
#[derive(Deserialize, Serialize)]
|
||||||
type Error = Error;
|
pub struct ErrorResponse<T> {
|
||||||
type Future = FutureResult<HttpResponse, Error>;
|
message: T,
|
||||||
|
}
|
||||||
|
|
||||||
fn respond_to(self, req: &HttpRequest) -> Self::Future {
|
pub struct RubbleResponder;
|
||||||
match self {
|
|
||||||
RubbleResponder::Html(content) => ok(HttpResponse::Ok()
|
impl RubbleResponder {
|
||||||
|
pub fn html(content: impl Into<String>) -> HttpResponse {
|
||||||
|
HttpResponse::Ok()
|
||||||
.content_type("text/html; charset=utf-8")
|
.content_type("text/html; charset=utf-8")
|
||||||
.body(content)),
|
.body(content.into())
|
||||||
RubbleResponder::Redirect(to) => ok(HttpResponse::Found()
|
|
||||||
.header(http::header::LOCATION, to)
|
|
||||||
.finish()),
|
|
||||||
RubbleResponder::NotFound => ok(HttpResponse::NotFound().finish()),
|
|
||||||
RubbleResponder::RedirectPermanently(to) => ok(HttpResponse::MovedPermanently()
|
|
||||||
.header(http::header::LOCATION, to)
|
|
||||||
.finish()),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn json(data: impl Serialize) -> HttpResponse {
|
||||||
|
HttpResponse::Ok()
|
||||||
|
.header(
|
||||||
|
http::header::CONTENT_TYPE,
|
||||||
|
"application/json; charset=utf-8",
|
||||||
|
)
|
||||||
|
.json(JsonResponse { data })
|
||||||
|
}
|
||||||
|
pub fn text(content: impl Into<String>) -> HttpResponse {
|
||||||
|
HttpResponse::Ok().body(content.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn redirect(to: impl Into<String>) -> HttpResponse {
|
||||||
|
HttpResponse::Found()
|
||||||
|
.header(http::header::LOCATION, to.into())
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn redirect_permanently(to: impl Into<String>) -> HttpResponse {
|
||||||
|
HttpResponse::MovedPermanently()
|
||||||
|
.header(http::header::LOCATION, to.into())
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn not_found() -> HttpResponse {
|
||||||
|
HttpResponse::NotFound().finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn unauthorized(reason: impl Serialize) -> HttpResponse {
|
||||||
|
HttpResponse::Unauthorized().json(&ErrorResponse { message: reason })
|
||||||
|
}
|
||||||
|
pub fn bad_gateway(reason: impl Serialize) -> HttpResponse {
|
||||||
|
HttpResponse::BadGateway().json(&ErrorResponse { message: reason })
|
||||||
|
}
|
||||||
|
pub fn bad_request(reason: impl Serialize) -> HttpResponse {
|
||||||
|
HttpResponse::BadRequest().json(&ErrorResponse { message: reason })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn routes() -> Scope {
|
||||||
|
web::scope("/")
|
||||||
|
.service(
|
||||||
|
web::scope("/api")
|
||||||
|
.service(web::scope("/users").service(api::user::admin_authentication))
|
||||||
|
.service(
|
||||||
|
web::scope("/articles")
|
||||||
|
.service(api::article::get_all_article)
|
||||||
|
.service(api::article::get_all_article_by_id)
|
||||||
|
.service(api::article::crate_article)
|
||||||
|
.service(api::article::update_article_by_id)
|
||||||
|
.service(api::article::delete_article_by_id),
|
||||||
|
)
|
||||||
|
.service(
|
||||||
|
web::scope("/settings")
|
||||||
|
.service(api::setting::get_settings)
|
||||||
|
.service(api::setting::update_setting_by_key),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.service(article::homepage)
|
||||||
|
.service(article::single_article)
|
||||||
|
.service(actix_files::Files::new(
|
||||||
|
"/statics",
|
||||||
|
"./templates/resources/",
|
||||||
|
))
|
||||||
|
.service(rss::rss_)
|
||||||
|
.service(article::get_article_by_url)
|
||||||
|
}
|
||||||
|
@ -7,12 +7,13 @@ use rss::{Channel, ChannelBuilder, Item, ItemBuilder};
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
#[get("/rss")]
|
#[get("/rss")]
|
||||||
pub fn rss_page(data: web::Data<RubbleData>) -> impl Responder {
|
pub fn rss_(data: web::Data<RubbleData>) -> impl Responder {
|
||||||
let articles = Article::read(&data.postgres());
|
let articles = Article::read(&data.postgres());
|
||||||
let setting = Setting::load(&data.postgres());
|
let setting = Setting::load(&data.postgres());
|
||||||
|
|
||||||
let items: Vec<Item> = articles
|
let items: Vec<Item> = articles
|
||||||
.iter()
|
.iter()
|
||||||
|
.filter(|article| article.published == true)
|
||||||
.map(ArticleView::from)
|
.map(ArticleView::from)
|
||||||
.map(|item| {
|
.map(|item| {
|
||||||
ItemBuilder::default()
|
ItemBuilder::default()
|
||||||
|
42
src/utils/jwt.rs
Normal file
42
src/utils/jwt.rs
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
use crate::models::user::User;
|
||||||
|
use crate::RANDOM_TOKEN_KEY;
|
||||||
|
use chrono::prelude::*;
|
||||||
|
use jsonwebtoken::{decode as jwt_decode, encode as jwt_encode, Algorithm, Header, Validation};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::ops::Add;
|
||||||
|
use time::Duration;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct JWTClaims {
|
||||||
|
iat: usize,
|
||||||
|
sub: String,
|
||||||
|
exp: usize,
|
||||||
|
id: i32,
|
||||||
|
username: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl JWTClaims {
|
||||||
|
pub fn encode(user: &User) -> String {
|
||||||
|
let now: DateTime<Utc> = Utc::now();
|
||||||
|
let expire: DateTime<Utc> = Utc::now().add(Duration::days(7));
|
||||||
|
let claims = JWTClaims {
|
||||||
|
iat: now.timestamp() as usize,
|
||||||
|
sub: String::from("LOGIN_TOKEN"),
|
||||||
|
exp: expire.timestamp() as usize,
|
||||||
|
id: user.id,
|
||||||
|
username: user.username.clone(),
|
||||||
|
};
|
||||||
|
|
||||||
|
jwt_encode(&Header::default(), &claims, &RANDOM_TOKEN_KEY).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decode(token: String) -> Result<String, ()> {
|
||||||
|
let claims = jwt_decode::<JWTClaims>(
|
||||||
|
token.as_str(),
|
||||||
|
&RANDOM_TOKEN_KEY,
|
||||||
|
&Validation::new(Algorithm::HS256),
|
||||||
|
)
|
||||||
|
.map_err(|e| ())?;
|
||||||
|
Ok(claims.claims.username)
|
||||||
|
}
|
||||||
|
}
|
1
src/utils/mod.rs
Normal file
1
src/utils/mod.rs
Normal file
@ -0,0 +1 @@
|
|||||||
|
pub mod jwt;
|
Loading…
Reference in New Issue
Block a user