mirror-chhoto-url/actix/src/main.rs

175 lines
5.3 KiB
Rust
Raw Normal View History

2023-04-03 03:26:23 +00:00
use actix_files::{Files, NamedFile};
2023-04-08 07:52:16 +00:00
use actix_session::{storage::CookieSessionStore, Session, SessionMiddleware};
2023-04-03 03:26:23 +00:00
use actix_web::{
2023-04-08 07:52:16 +00:00
cookie::Key,
2023-04-28 05:08:16 +00:00
delete, get,
http::StatusCode,
middleware, post,
2023-04-03 03:26:23 +00:00
web::{self, Redirect},
App, Either, HttpResponse, HttpServer, Responder,
2023-04-03 03:26:23 +00:00
};
2023-04-03 23:52:17 +00:00
use rusqlite::Connection;
2023-04-26 19:40:54 +00:00
use std::env;
2023-04-08 07:52:16 +00:00
mod auth;
2023-04-03 03:26:23 +00:00
mod database;
mod utils;
2023-04-02 21:53:55 +00:00
2023-04-03 23:52:17 +00:00
// This struct represents state
struct AppState {
db: Connection,
}
2024-02-11 01:41:50 +00:00
// Store the version number
const VERSION: &str = env!("CARGO_PKG_VERSION");
2023-04-03 03:26:23 +00:00
// Define the routes
// Add new links
2023-04-03 22:40:37 +00:00
#[post("/api/new")]
2023-04-08 07:52:16 +00:00
async fn add_link(req: String, data: web::Data<AppState>, session: Session) -> HttpResponse {
if auth::validate(session) {
let out = utils::add_link(req, &data.db);
if out.0 {
2024-04-03 21:19:39 +00:00
HttpResponse::Created().body(out.1)
2023-04-08 07:52:16 +00:00
} else {
2024-04-03 21:19:39 +00:00
HttpResponse::Conflict().body(out.1)
2023-04-08 07:52:16 +00:00
}
2023-04-03 22:40:37 +00:00
} else {
2024-04-03 21:19:39 +00:00
HttpResponse::Unauthorized().body("Not logged in!")
2023-04-03 22:40:37 +00:00
}
}
2023-04-03 03:26:23 +00:00
// Return all active links
2023-04-03 16:55:27 +00:00
#[get("/api/all")]
2023-04-08 07:52:16 +00:00
async fn getall(data: web::Data<AppState>, session: Session) -> HttpResponse {
if auth::validate(session) {
HttpResponse::Ok().body(utils::getall(&data.db))
} else {
2024-04-03 21:19:39 +00:00
HttpResponse::Unauthorized().body("Not logged in!")
2023-04-08 07:52:16 +00:00
}
2023-04-03 16:55:27 +00:00
}
2023-04-03 18:50:23 +00:00
// Get the site URL
#[get("/api/siteurl")]
2023-04-08 07:52:16 +00:00
async fn siteurl(session: Session) -> HttpResponse {
if auth::validate(session) {
let site_url = env::var("site_url").unwrap_or(String::from("unset"));
2023-04-08 07:52:16 +00:00
HttpResponse::Ok().body(site_url)
} else {
2024-04-03 21:19:39 +00:00
HttpResponse::Unauthorized().body("Not logged in!")
2023-04-08 07:52:16 +00:00
}
2023-04-03 18:50:23 +00:00
}
2024-02-11 01:41:50 +00:00
// Get the version number
#[get("/api/version")]
async fn version() -> HttpResponse {
HttpResponse::Ok().body(VERSION)
}
2023-04-03 03:26:23 +00:00
// 404 error page
async fn error404() -> impl Responder {
2023-04-28 17:12:31 +00:00
NamedFile::open_async("./resources/static/404.html")
.await
.customize()
.with_status(StatusCode::NOT_FOUND)
2023-04-03 03:26:23 +00:00
}
// Handle a given shortlink
#[get("/{shortlink}")]
2023-04-03 23:52:17 +00:00
async fn link_handler(shortlink: web::Path<String>, data: web::Data<AppState>) -> impl Responder {
2023-04-03 22:40:37 +00:00
let shortlink_str = shortlink.to_string();
if let Some(longlink) = utils::get_longurl(shortlink_str, &data.db) {
let redirect_method = env::var("redirect_method").unwrap_or(String::from("PERMANENT"));
2023-04-03 23:52:17 +00:00
database::add_hit(shortlink.as_str(), &data.db);
if redirect_method == "TEMPORARY" {
Either::Left(Redirect::to(longlink))
2023-04-28 05:22:30 +00:00
} else {
// Defaults to permanent redirection
Either::Left(Redirect::to(longlink).permanent())
2023-04-28 05:22:30 +00:00
}
} else {
Either::Right(
NamedFile::open_async("./resources/static/404.html")
.await
.customize()
.with_status(StatusCode::NOT_FOUND),
)
2023-04-03 03:26:23 +00:00
}
2023-04-02 21:53:55 +00:00
}
2023-04-08 07:52:16 +00:00
// Handle login
#[post("/api/login")]
async fn login(req: String, session: Session) -> HttpResponse {
if let Ok(password) = env::var("password") {
if password != req {
eprintln!("Failed login attempt!");
2024-04-03 21:19:39 +00:00
return HttpResponse::Unauthorized().body("Wrong password!");
}
2023-04-08 07:52:16 +00:00
}
// Return Ok if no password was set on the server side
session
.insert("chhoto-url-auth", auth::gen_token())
.expect("Error inserting auth token.");
HttpResponse::Ok().body("Correct password!")
2023-04-08 07:52:16 +00:00
}
2023-04-03 22:58:19 +00:00
// Delete a given shortlink
#[delete("/api/del/{shortlink}")]
2023-04-08 07:52:16 +00:00
async fn delete_link(
shortlink: web::Path<String>,
data: web::Data<AppState>,
session: Session,
) -> HttpResponse {
if auth::validate(session) {
if utils::delete_link(shortlink.to_string(), &data.db) {
HttpResponse::Ok().body(format!("Deleted {shortlink}"))
} else {
HttpResponse::NotFound().body("Not found!")
}
2023-04-08 07:52:16 +00:00
} else {
2024-04-03 21:19:39 +00:00
HttpResponse::Unauthorized().body("Not logged in!")
2023-04-08 07:52:16 +00:00
}
2023-04-03 22:58:19 +00:00
}
2023-04-03 03:26:23 +00:00
#[actix_web::main]
2023-04-02 21:53:55 +00:00
async fn main() -> std::io::Result<()> {
2023-04-10 16:51:20 +00:00
env_logger::init_from_env(env_logger::Env::new().default_filter_or("warn"));
// Generate session key in runtime so that restart invalidates older logins
2023-04-08 07:52:16 +00:00
let secret_key = Key::generate();
let db_location = env::var("db_url").unwrap_or(String::from("/urls.sqlite"));
let port = env::var("port")
.unwrap_or(String::from("4567"))
.parse::<u16>()
.expect("Supplied port is not an integer");
2023-04-08 20:36:33 +00:00
// Actually start the server
2023-04-08 07:52:16 +00:00
HttpServer::new(move || {
2023-04-02 21:53:55 +00:00
App::new()
2023-10-08 23:20:34 +00:00
.wrap(
SessionMiddleware::builder(CookieSessionStore::default(), secret_key.clone())
2024-04-02 23:07:29 +00:00
.cookie_same_site(actix_web::cookie::SameSite::Strict)
2023-10-08 23:20:34 +00:00
.cookie_secure(false)
.build(),
)
2023-04-08 20:36:33 +00:00
// Maintain a single instance of database throughout
2023-04-03 23:52:17 +00:00
.app_data(web::Data::new(AppState {
2023-04-08 20:36:33 +00:00
db: database::open_db(env::var("db_url").unwrap_or(db_location.clone())),
2023-04-03 23:52:17 +00:00
}))
2023-04-10 16:51:20 +00:00
.wrap(middleware::Logger::default())
2023-04-04 00:24:33 +00:00
.wrap(middleware::Compress::default())
2023-04-03 03:26:23 +00:00
.service(link_handler)
2023-04-03 16:55:27 +00:00
.service(getall)
2023-04-03 18:50:23 +00:00
.service(siteurl)
2024-02-11 01:41:50 +00:00
.service(version)
2023-04-03 22:40:37 +00:00
.service(add_link)
2023-04-03 22:58:19 +00:00
.service(delete_link)
2023-04-08 07:52:16 +00:00
.service(login)
2023-05-06 05:34:56 +00:00
.service(Files::new("/", "./resources/").index_file("index.html"))
.default_service(web::get().to(error404))
2023-04-02 21:53:55 +00:00
})
.bind(("0.0.0.0", port))?
2023-04-02 21:53:55 +00:00
.run()
.await
}