new laptop setup
parent
5b95621d04
commit
31b47e892d
|
@ -0,0 +1,36 @@
|
|||
body {
|
||||
background-color: #92a8d1;
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
height: 100vh;
|
||||
}
|
||||
@media(max-width: 500px) {
|
||||
body {
|
||||
padding: 1px;
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
@media(min-width: 501px) and (max-width: 550px) {
|
||||
body {
|
||||
padding: 1px;
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 5fr 1fr;
|
||||
}
|
||||
.mainContainer {grid-column-start: 2;}
|
||||
}
|
||||
@media(min-width: 551px) and (max-width: 1000px) {
|
||||
body {
|
||||
padding: 1px;
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 3fr 1fr;
|
||||
}
|
||||
.mainContainer {grid-column-start: 2;}
|
||||
}
|
||||
@media(min-width: 1001px) {
|
||||
body {
|
||||
padding: 1px;
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr 1fr;
|
||||
}
|
||||
.mainContainer {grid-column-start: 2;}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
.itemContainer {
|
||||
background: #034f84;
|
||||
margin: 0.3rem;
|
||||
}
|
||||
.itemContainer:hover {
|
||||
background: #034f99;
|
||||
}
|
||||
.itemContainer p {
|
||||
color: white;
|
||||
display: inline-block;
|
||||
margin: 0.5rem;
|
||||
margin-right: 0.4rem;
|
||||
margin-left: 0.4rem;
|
||||
}
|
||||
.actionButton {
|
||||
display: inline-block;
|
||||
float: right;
|
||||
background: #f7786b;
|
||||
border: none;
|
||||
padding: 0.5rem;
|
||||
padding-left: 2rem;
|
||||
padding-right: 2rem;
|
||||
color: white;
|
||||
}
|
||||
.actionButton:hover {
|
||||
background: #f7686b;
|
||||
color: black;
|
||||
}
|
||||
.inputContainer {
|
||||
background: #034f84;
|
||||
margin: 0.3rem;
|
||||
margin-top: 2rem;
|
||||
}
|
||||
.inputContainer input {
|
||||
display: inline-block;
|
||||
margin: 0.3rem
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
const loginButton = document.getElementById('loginButton');
|
||||
const username = document.getElementById(
|
||||
'defaultLoginFormUsername');
|
||||
const password = document.getElementById(
|
||||
'defaultLoginFormPassword');
|
||||
const message = document.getElementById("loginMessage");
|
||||
|
||||
|
||||
loginButton.addEventListener("click", () => {
|
||||
let xhr = new XMLHttpRequest();
|
||||
xhr.open("POST", "/api/v1/auth/login", true);
|
||||
xhr.setRequestHeader("Content-Type",
|
||||
"application/json");
|
||||
xhr.onreadystatechange = function () {
|
||||
if (xhr.readyState === 4) {
|
||||
if (xhr.status === 200) {
|
||||
let token = xhr.getResponseHeader("token");
|
||||
localStorage.setItem("user-token", token);
|
||||
console.log("status 200: " + document.location.origin);
|
||||
window.location.replace(
|
||||
document.location.origin);
|
||||
} else {
|
||||
message.innerText =
|
||||
"login failed please try again";
|
||||
}
|
||||
}
|
||||
};
|
||||
let data = JSON.stringify({
|
||||
"username": username.value,
|
||||
"password": password.value
|
||||
});
|
||||
|
||||
xhr.send(data);
|
||||
message.innerText = "logging in";
|
||||
|
||||
})
|
|
@ -0,0 +1,35 @@
|
|||
if (localStorage.getItem("user-token") == null) {
|
||||
window.location.replace(document.location.origin + "/login");
|
||||
} else {
|
||||
getArticles();
|
||||
}
|
||||
|
||||
function apiCall(url, method) {
|
||||
let xhr = new XMLHttpRequest();
|
||||
xhr.withCredentials = true;
|
||||
|
||||
xhr.addEventListener("readystatechange", function () {
|
||||
if (this.readyState === this.DONE) {
|
||||
if (this.status === 401) {
|
||||
window.location.replace(document.location.origin + "/login/");
|
||||
} else {
|
||||
runRenderProcess(JSON.parse(this.responseText));
|
||||
localStorage.setItem("item-cache-date", new Date());
|
||||
localStorage.setItem("item-cache-data", this.responseText);
|
||||
}
|
||||
}
|
||||
});
|
||||
xhr.open(method, "/api/v1" + url);
|
||||
xhr.setRequestHeader("content-type", "application/json");
|
||||
xhr.setRequestHeader("user-token", localStorage.getItem("user-token"));
|
||||
return xhr;
|
||||
}
|
||||
|
||||
function runRenderProcess(params) {
|
||||
document.getElementById("mainContainer").innerHtml = params;
|
||||
}
|
||||
|
||||
function getArticles() {
|
||||
let call = apiCall("/article/get", "GET");
|
||||
call.send();
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
-- This file should undo anything in `up.sql`
|
||||
DROP TABLE feed_item
|
|
@ -0,0 +1,8 @@
|
|||
-- Your SQL goes here
|
||||
CREATE TABLE feed (
|
||||
id SERIAL PRIMARY KEY,
|
||||
user_id INTEGER NOT NULL REFERENCES users(id) DEFERRABLE INITIALLY DEFERRED,
|
||||
title VARCHAR NOT NULL,
|
||||
url VARCHAR NOT NULL,
|
||||
UNIQUE (url)
|
||||
)
|
|
@ -0,0 +1,4 @@
|
|||
-- This file should undo anything in `up.sql`
|
||||
CREATE TABLE feed_item {
|
||||
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
-- Your SQL goes here
|
||||
CREATE TABLE feed_item (
|
||||
id SERIAL PRIMARY KEY,
|
||||
feed_id INTEGER NOT NULL REFERENCES feed(id) DEFERRABLE INITIALLY DEFERRED,
|
||||
content TEXT NOT NULL,
|
||||
read BOOLEAN NOT NULL DEFAULT FALSE
|
||||
)
|
|
@ -0,0 +1,104 @@
|
|||
extern crate hmac;
|
||||
extern crate jwt;
|
||||
extern crate sha2;
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use actix_web::HttpRequest;
|
||||
use hmac::{Hmac, Mac};
|
||||
use jwt::{Header, SignWithKey, Token, VerifyWithKey};
|
||||
use sha2::Sha256;
|
||||
|
||||
pub struct JwtToken {
|
||||
pub user_id: i32,
|
||||
pub body: String,
|
||||
}
|
||||
|
||||
type HmacSha256 = Hmac<Sha256>;
|
||||
|
||||
impl JwtToken {
|
||||
pub fn encode(user_id: i32) -> String {
|
||||
let key: HmacSha256 = HmacSha256::new_from_slice(b"secret").unwrap();
|
||||
let mut claims = BTreeMap::new();
|
||||
claims.insert("user_id", user_id);
|
||||
claims.sign_with_key(&key).unwrap()
|
||||
}
|
||||
|
||||
pub fn decode(encoded_token: String) -> Result<JwtToken, &'static str> {
|
||||
let key: HmacSha256 = HmacSha256::new_from_slice(b"secret").unwrap();
|
||||
let token_str: &str = encoded_token.as_str();
|
||||
let token: Result<Token<Header, BTreeMap<String, i32>, jwt::Verified>, jwt::Error> =
|
||||
VerifyWithKey::verify_with_key(token_str, &key);
|
||||
|
||||
match token {
|
||||
Ok(token) => {
|
||||
let _header = token.header();
|
||||
let claims = token.claims();
|
||||
Ok(JwtToken {
|
||||
user_id: claims["user_id"],
|
||||
body: encoded_token,
|
||||
})
|
||||
}
|
||||
Err(_err) => Err("could not decode token"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn decode_from_request(request: HttpRequest) -> Result<JwtToken, &'static str> {
|
||||
match request.headers().get("user-token") {
|
||||
Some(token) => JwtToken::decode(String::from(token.to_str().unwrap())),
|
||||
None => Err("There is no token"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod jwt_test {
|
||||
use actix_web::{http::header, test};
|
||||
|
||||
use super::JwtToken;
|
||||
|
||||
#[test]
|
||||
async fn encode_decode() {
|
||||
let encoded_token: String = JwtToken::encode(32);
|
||||
let decoded_token: JwtToken = JwtToken::decode(encoded_token).unwrap();
|
||||
assert_eq!(32, decoded_token.user_id);
|
||||
}
|
||||
|
||||
#[test]
|
||||
async fn decode_incorrect_token() {
|
||||
let encoded_token: String = String::from("test");
|
||||
|
||||
match JwtToken::decode(encoded_token) {
|
||||
Err(message) => assert_eq!(message, "could not decode token"),
|
||||
_ => panic!("Incorrect token should not be able to decode."),
|
||||
}
|
||||
}
|
||||
|
||||
#[actix_web::test]
|
||||
async fn decode_from_request_with_correct_token() {
|
||||
let encoded_token: String = JwtToken::encode(32);
|
||||
let request = test::TestRequest::default()
|
||||
.insert_header(header::ContentType::json())
|
||||
.insert_header(("user-token", encoded_token))
|
||||
.to_http_request();
|
||||
let out_come = JwtToken::decode_from_request(request);
|
||||
|
||||
match out_come {
|
||||
Ok(token) => assert_eq!(32, token.user_id),
|
||||
_ => panic!("Token is not returned with it should be."),
|
||||
}
|
||||
}
|
||||
|
||||
#[actix_web::test]
|
||||
async fn decode_from_request_with_no_token() {
|
||||
let request = test::TestRequest::default()
|
||||
.insert_header(("test", "test"))
|
||||
.to_http_request();
|
||||
let out_come = JwtToken::decode_from_request(request);
|
||||
|
||||
match out_come {
|
||||
Err(message) => assert_eq!("There is no token", message),
|
||||
_ => panic!("Token should not be returned when it is not present in the header."),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
use actix_web::dev::ServiceRequest;
|
||||
pub mod jwt;
|
||||
pub mod processes;
|
||||
use crate::auth::processes::check_password;
|
||||
use crate::auth::processes::extract_header_token;
|
||||
|
||||
pub fn process_token(request: &ServiceRequest) -> Result<String, &'static str> {
|
||||
match extract_header_token(request) {
|
||||
Ok(token) => check_password(token),
|
||||
Err(message) => Err(message),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod mod_test {
|
||||
|
||||
use actix_web::test::TestRequest;
|
||||
|
||||
use super::{jwt::JwtToken, process_token};
|
||||
|
||||
#[test]
|
||||
fn process_token_test() {
|
||||
let token = JwtToken::encode(32);
|
||||
let request = TestRequest::delete()
|
||||
.insert_header(("user-token", token))
|
||||
.to_srv_request();
|
||||
|
||||
match process_token(&request) {
|
||||
Ok(message) => assert_eq!("passed", message),
|
||||
Err(_) => panic!("process token failed"),
|
||||
}
|
||||
}
|
||||
|
||||
#[actix_web::test]
|
||||
async fn process_token_not_existing() {
|
||||
let request = TestRequest::default().to_srv_request();
|
||||
|
||||
match process_token(&request) {
|
||||
Err(error) => assert_eq!("there is no token", error),
|
||||
_ => panic!("Not existing token should not be processes"),
|
||||
}
|
||||
}
|
||||
|
||||
#[actix_web::test]
|
||||
async fn process_wrong_token() {
|
||||
let request = TestRequest::default()
|
||||
.insert_header(("user-token", "bla"))
|
||||
.to_srv_request();
|
||||
|
||||
match process_token(&request) {
|
||||
Err(error) => assert_eq!("could not decode token", error),
|
||||
_ => panic!("Not existing token should not be processes"),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
|
||||
use super::jwt;
|
||||
use actix_web::dev::ServiceRequest;
|
||||
|
||||
pub fn check_password(password: String) -> Result<String, &'static str> {
|
||||
match jwt::JwtToken::decode(password) {
|
||||
Ok(_token) => Ok(String::from("passed")),
|
||||
Err(message) => Err(message),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn extract_header_token(request: &ServiceRequest) -> Result<String, &'static str> {
|
||||
match request.headers().get("user-token") {
|
||||
Some(token) => match token.to_str() {
|
||||
Ok(processed_password) => Ok(String::from(processed_password)),
|
||||
Err(_processed_password) => Err("there was an error processing token"),
|
||||
},
|
||||
None => Err("there is no token"),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod processes_test {
|
||||
use actix_web::test::TestRequest;
|
||||
|
||||
use crate::auth::jwt::JwtToken;
|
||||
|
||||
use super::check_password;
|
||||
|
||||
#[test]
|
||||
fn check_correct_password() {
|
||||
let password_string: String = JwtToken::encode(32);
|
||||
|
||||
let result = check_password(password_string);
|
||||
|
||||
match result {
|
||||
Ok(check) => assert_eq!("passed", check),
|
||||
_ => panic!("Check correct password failed."),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn incorrect_check_password() {
|
||||
let password: String = String::from("test");
|
||||
|
||||
match check_password(password) {
|
||||
Err(message) => assert_eq!("could not decode token", message),
|
||||
_ => panic!("check password should not be able to be decoded"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn successful_extract_header_token() {
|
||||
let request = TestRequest::default()
|
||||
.insert_header(("user-token", "token"))
|
||||
.to_srv_request();
|
||||
|
||||
match super::extract_header_token(&request) {
|
||||
Ok(processed_password) => assert_eq!("token", processed_password),
|
||||
_ => panic!("failed extract_header_token"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn failed_extract_header_token() {
|
||||
let request = TestRequest::default()
|
||||
.insert_header(("wrong", "bla"))
|
||||
.to_srv_request();
|
||||
|
||||
match super::extract_header_token(&request) {
|
||||
Err(processed_password) => assert_eq!("there is no token", processed_password),
|
||||
_ => panic!("Extract header token should fail when not provided."),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
use diesel::pg::PgConnection;
|
||||
use diesel::prelude::*;
|
||||
use dotenv::dotenv;
|
||||
use std::env;
|
||||
|
||||
pub fn establish_connection() -> PgConnection {
|
||||
dotenv().ok();
|
||||
|
||||
let database_url = env::var("DATABASE_URL").expect("DATABASE_URL must be set");
|
||||
PgConnection::establish(&database_url)
|
||||
.unwrap_or_else(|_| panic!("Error connecting to database {}", database_url))
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
use serde::Deserialize;
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
pub struct Login {
|
||||
pub username: String,
|
||||
pub password: String,
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
pub mod login;
|
||||
pub mod new_user;
|
|
@ -0,0 +1,8 @@
|
|||
use serde::Deserialize;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct NewUserSchema {
|
||||
pub name: String,
|
||||
pub email: String,
|
||||
pub password: String,
|
||||
}
|
|
@ -3,9 +3,7 @@ extern crate dotenv;
|
|||
|
||||
use actix_service::Service;
|
||||
use actix_web::{App, HttpResponse, HttpServer};
|
||||
use env_logger;
|
||||
use futures::future::{ok, Either};
|
||||
use log;
|
||||
mod auth;
|
||||
mod database;
|
||||
mod json_serialization;
|
||||
|
@ -31,6 +29,7 @@ async fn main() -> std::io::Result<()> {
|
|||
Err(_message) => passed = false,
|
||||
}
|
||||
} else {
|
||||
log::warn!("No auth check done.");
|
||||
passed = true;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
use super::super::feed;
|
||||
use super::super::user::user::User;
|
||||
use diesel::{Associations, Identifiable, Queryable};
|
||||
|
||||
#[derive(Queryable, Identifiable, Associations)]
|
||||
#[diesel(belongs_to(User))]
|
||||
#[diesel(table_name=feed)]
|
||||
pub struct Feed {
|
||||
pub id: i32,
|
||||
pub user_id: i32,
|
||||
pub title: String,
|
||||
pub url: String,
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
pub mod feed;
|
|
@ -0,0 +1,2 @@
|
|||
|
||||
|
|
@ -0,0 +1 @@
|
|||
mod feed_item;
|
|
@ -0,0 +1,9 @@
|
|||
use actix_web::{HttpRequest, HttpResponse};
|
||||
use crate::auth::jwt::JwtToken;
|
||||
|
||||
|
||||
pub async fn get(req: HttpRequest) -> HttpResponse {
|
||||
let token: JwtToken = JwtToken::decode_from_request(req).unwrap();
|
||||
|
||||
todo!();
|
||||
}
|
|
@ -1 +1,16 @@
|
|||
use actix_web::web;
|
||||
|
||||
use crate::views::path::Path;
|
||||
pub mod feeds;
|
||||
mod get;
|
||||
|
||||
pub fn feed_factory(app: &mut web::ServiceConfig) {
|
||||
let base_path: Path = Path {
|
||||
prefix: String::from("/article"),
|
||||
backend: true,
|
||||
};
|
||||
app.route(
|
||||
&base_path.define(String::from("/get")),
|
||||
web::get().to(get::get),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
use std::fs;
|
||||
|
||||
pub fn read_file(file_path: &str) -> String {
|
||||
let data: String = fs::read_to_string(file_path)
|
||||
.expect(format!("Unable to read file {}", file_path).as_str());
|
||||
return data;
|
||||
}
|
||||
|
||||
pub fn add_component(component_tag: String, html_data: String) -> String {
|
||||
let css_tag: String = component_tag.to_uppercase() + &String::from("_CSS");
|
||||
let html_tag: String = component_tag.to_uppercase() + &String::from("_HTML");
|
||||
let css_path = String::from("./templates/components/")
|
||||
+ &component_tag.to_lowercase()
|
||||
+ &String::from(".css");
|
||||
let css_loaded = read_file(&css_path);
|
||||
let html_path = String::from("./templates/components/")
|
||||
+ &component_tag.to_lowercase()
|
||||
+ &String::from(".html");
|
||||
let html_loaded = read_file(&html_path);
|
||||
let html_data = html_data.replace(html_tag.as_str(), &html_loaded);
|
||||
let html_data = html_data.replace(css_tag.as_str(),&css_loaded);
|
||||
return html_data
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
use super::content_loader::read_file;
|
||||
use actix_web::HttpResponse;
|
||||
|
||||
pub async fn login() -> HttpResponse {
|
||||
let mut html_data = read_file(&String::from("./templates/login.html"));
|
||||
let javascript_data = read_file(&String::from("./javascript/login.js"));
|
||||
let css_data = read_file(&String::from("./css/main.css"));
|
||||
let base_css_data = read_file(&String::from("./css/base.css"));
|
||||
|
||||
html_data = html_data.replace("{{JAVASCRIPT}}", &javascript_data);
|
||||
html_data = html_data.replace("{{CSS}}", &css_data);
|
||||
html_data = html_data.replace("{{BASE_CSS}}", &base_css_data);
|
||||
|
||||
HttpResponse::Ok()
|
||||
.content_type("text/html; charset=utf-8")
|
||||
.body(html_data)
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
use actix_web::HttpResponse;
|
||||
|
||||
pub async fn logout() -> HttpResponse {
|
||||
HttpResponse::Ok()
|
||||
.content_type("text/html; charset=utf-8")
|
||||
.body(
|
||||
"<html>\
|
||||
<script>\
|
||||
localStorage.removeItem('user-token'); \
|
||||
window.location.replace(document.location.origin);\
|
||||
</script>\
|
||||
</html>
|
||||
",
|
||||
)
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
use actix_web::web;
|
||||
mod content_loader;
|
||||
mod login;
|
||||
mod logout;
|
||||
mod reader;
|
||||
use super::path::Path;
|
||||
|
||||
/// This function adds the app views to the web server serving HTML.
|
||||
///
|
||||
/// # Arguments
|
||||
/// * (&mut web::ServiceConfig): reference to the app for configuration
|
||||
///
|
||||
/// # Returns
|
||||
/// None
|
||||
pub fn app_factory(app: &mut web::ServiceConfig) {
|
||||
// define the path struct
|
||||
let base_path: Path = Path {
|
||||
prefix: String::from("/"),
|
||||
backend: false,
|
||||
};
|
||||
// define the routes for the app
|
||||
|
||||
app.route(
|
||||
&base_path.define(String::from("")),
|
||||
web::get().to(reader::reader),
|
||||
);
|
||||
app.route(
|
||||
&base_path.define(String::from("login")),
|
||||
web::get().to(login::login),
|
||||
);
|
||||
app.route(
|
||||
&base_path.define(String::from("logout")),
|
||||
web::get().to(logout::logout),
|
||||
);
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
use super::content_loader::{add_component, read_file};
|
||||
use actix_web::HttpResponse;
|
||||
|
||||
pub async fn reader() -> HttpResponse {
|
||||
let mut html_data = read_file("./templates/reader.html");
|
||||
let javascript = read_file("./javascript/main.js");
|
||||
let css = read_file("./css/main.css");
|
||||
let base_css = read_file("./css/base.css");
|
||||
|
||||
html_data = html_data.replace("JAVASCRIPT", &javascript);
|
||||
html_data = html_data.replace("CSS", &css);
|
||||
html_data = html_data.replace("BASE_CSS", &base_css);
|
||||
// html_data = add_component(String::from("header"), html_data);
|
||||
|
||||
HttpResponse::Ok()
|
||||
.content_type("text/html; charset=utf-8")
|
||||
.body(html_data)
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
use crate::database::establish_connection;
|
||||
use crate::diesel;
|
||||
use crate::json_serialization::login::Login;
|
||||
use crate::models::user::user::User;
|
||||
use crate::schema::users;
|
||||
use crate::{auth::jwt::JwtToken, schema::users::username};
|
||||
use actix_web::{web, HttpResponse};
|
||||
use diesel::prelude::*;
|
||||
|
||||
pub async fn login(credentials: web::Json<Login>) -> HttpResponse {
|
||||
let username_cred: String = credentials.username.clone();
|
||||
let password: String = credentials.password.clone();
|
||||
|
||||
let mut connection = establish_connection();
|
||||
|
||||
let users = users::table
|
||||
.filter(username.eq(username_cred.as_str()))
|
||||
.load::<User>(&mut connection)
|
||||
.unwrap();
|
||||
|
||||
if users.is_empty() {
|
||||
return HttpResponse::NotFound().await.unwrap();
|
||||
} else if users.len() > 1 {
|
||||
log::error!(
|
||||
"multiple user have the usernam: {}",
|
||||
credentials.username.clone()
|
||||
);
|
||||
return HttpResponse::Conflict().await.unwrap();
|
||||
}
|
||||
|
||||
let user: &User = &users[0];
|
||||
|
||||
match user.clone().verify(password) {
|
||||
true => {
|
||||
log::info!("verified password successfully");
|
||||
let token: String = JwtToken::encode(user.clone().id);
|
||||
HttpResponse::Ok()
|
||||
.insert_header(("token", token))
|
||||
.await
|
||||
.unwrap()
|
||||
}
|
||||
false => HttpResponse::Unauthorized().await.unwrap(),
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
pub async fn logout() -> String {
|
||||
format!("logout view")
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
use actix_web::web;
|
||||
use actix_web::web::ServiceConfig;
|
||||
|
||||
use super::path::Path;
|
||||
|
||||
mod login;
|
||||
mod logout;
|
||||
|
||||
pub fn auth_factory(app: &mut ServiceConfig) {
|
||||
let base_path: Path = Path {
|
||||
prefix: String::from("/auth"),
|
||||
backend: true,
|
||||
};
|
||||
|
||||
app.route(
|
||||
&base_path.define(String::from("/login")),
|
||||
web::post().to(login::login),
|
||||
);
|
||||
app.route(
|
||||
&base_path.define(String::from("/logout")),
|
||||
web::post().to(logout::logout),
|
||||
);
|
||||
}
|
|
@ -1,10 +1,14 @@
|
|||
use actix_web::web;
|
||||
|
||||
use crate::reader;
|
||||
mod app;
|
||||
mod auth;
|
||||
pub(crate) mod path;
|
||||
mod users;
|
||||
|
||||
pub fn views_factory(app: &mut web::ServiceConfig) {
|
||||
// auth::auth_factory(app);
|
||||
// to_do::item_factory(app);
|
||||
// app::app_factory(app);
|
||||
auth::auth_factory(app);
|
||||
app::app_factory(app);
|
||||
users::user_factory(app);
|
||||
reader::feed_factory(app);
|
||||
}
|
||||
|
|
|
@ -14,3 +14,4 @@ impl Path {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
use crate::database::establish_connection;
|
||||
use crate::diesel;
|
||||
use crate::json_serialization::new_user::NewUserSchema;
|
||||
use crate::models::user::new_user::NewUser;
|
||||
use crate::schema::users;
|
||||
use actix_web::{web, HttpResponse};
|
||||
use diesel::prelude::*;
|
||||
|
||||
pub async fn create(new_user: web::Json<NewUserSchema>) -> HttpResponse {
|
||||
let mut connection = establish_connection();
|
||||
let name: String = new_user.name.clone();
|
||||
let email: String = new_user.email.clone();
|
||||
let new_password: String = new_user.password.clone();
|
||||
|
||||
let new_user = NewUser::new(name, email, new_password);
|
||||
|
||||
let insert_result = diesel::insert_into(users::table)
|
||||
.values(&new_user)
|
||||
.execute(&mut connection);
|
||||
|
||||
match insert_result {
|
||||
Ok(_) => HttpResponse::Created().await.unwrap(),
|
||||
Err(_) => HttpResponse::Conflict().await.unwrap(),
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
use super::path::Path;
|
||||
use actix_web::web;
|
||||
mod create;
|
||||
|
||||
pub fn user_factory(app: &mut web::ServiceConfig) {
|
||||
let base_path: Path = Path {
|
||||
prefix: String::from("/user"),
|
||||
backend: true,
|
||||
};
|
||||
|
||||
app.route(
|
||||
&base_path.define(String::from("/create")),
|
||||
web::post().to(create::create),
|
||||
);
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
.header {
|
||||
background: #034f84;
|
||||
margin-bottom: 0.3rem;
|
||||
}
|
||||
|
||||
.header p {
|
||||
color: white;
|
||||
display: inline-block;
|
||||
margin: 0.5rem;
|
||||
margin-right: 0.4rem;
|
||||
margin-left: 0.4rem;
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
<div class="header">
|
||||
<p>complete tasks: </p><p id="completeNum"></p>
|
||||
<p>pending tasks: </p><p id="pendingNum"></p>
|
||||
</div>
|
|
@ -0,0 +1,51 @@
|
|||
<html>
|
||||
|
||||
<head>
|
||||
<meta charSet="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width,
|
||||
initial-scale=1.0" />
|
||||
<meta name="description" content="This is a simple to do app" />
|
||||
<meta httpEquiv="X-UA-Compatiable" content="ie=edge" />
|
||||
<title>Login</title>
|
||||
</head>
|
||||
|
||||
<style>
|
||||
{{BASE_CSS}}
|
||||
{{CSS}}
|
||||
|
||||
.loginButtonStyle {
|
||||
display: inline-block;
|
||||
background: #f7786b;
|
||||
border: none;
|
||||
padding: 0.5rem;
|
||||
padding-left: 2rem;
|
||||
padding-right: 2rem;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.loginButtonStyle:hover {
|
||||
background: #f7686b;
|
||||
color: black;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<body>
|
||||
<div class="mainContainer">
|
||||
<h2 class="ContainerTitle" style="text-align:center;">Login</h2>
|
||||
<p id="loginMessage" class="FeedbackMessage" style="text-align:center;"></p>
|
||||
<form style="text-align:center;" action="submit">
|
||||
<input type="text" value="" placeholder="Username" class="formInputContainer"
|
||||
id="defaultLoginFormUsername"><br>
|
||||
<p></p>
|
||||
<input type="password" value="" placeholder="Password" class="formInputContainer"
|
||||
id="defaultLoginFormPassword"><br><br>
|
||||
<input type="button" value="Submit" class="loginButtonStyle" id="loginButton" style="text-align:center;">
|
||||
</form>
|
||||
</div>
|
||||
</body>
|
||||
<script>
|
||||
{{JAVASCRIPT}}
|
||||
</script>
|
||||
|
||||
</html>
|
|
@ -0,0 +1,26 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta
|
||||
name="viewport"
|
||||
content="width=device-width,
|
||||
initial-scale=1.0"
|
||||
/>
|
||||
<meta httpEquiv="X-UA-Compatible" content="ie=edge" />
|
||||
<meta name="description" content="This is a simple to do app" />
|
||||
<title>ToDo App</title>
|
||||
</head>
|
||||
<style>
|
||||
BASE_CSS
|
||||
CSS
|
||||
HEADER_CSS
|
||||
</style>
|
||||
|
||||
<body>
|
||||
<div class="mainContainer"></div>
|
||||
</body>
|
||||
<script>
|
||||
JAVASCRIPT;
|
||||
</script>
|
||||
</html>
|
Loading…
Reference in New Issue