2023-04-03 18:50:23 +00:00
|
|
|
use std::env;
|
|
|
|
|
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-04 00:24:33 +00:00
|
|
|
delete, get, middleware, post,
|
2023-04-03 03:26:23 +00:00
|
|
|
web::{self, Redirect},
|
2023-04-03 16:55:27 +00:00
|
|
|
App, HttpResponse, HttpServer, Responder,
|
2023-04-03 03:26:23 +00:00
|
|
|
};
|
2023-04-03 23:52:17 +00:00
|
|
|
use rusqlite::Connection;
|
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,
|
|
|
|
}
|
|
|
|
|
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 {
|
|
|
|
HttpResponse::Ok().body(out.1)
|
|
|
|
} else {
|
|
|
|
HttpResponse::BadRequest().body(out.1)
|
|
|
|
}
|
2023-04-03 22:40:37 +00:00
|
|
|
} else {
|
2023-04-08 07:52:16 +00:00
|
|
|
HttpResponse::Forbidden().body("logged_out")
|
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 {
|
|
|
|
HttpResponse::Forbidden().body("logged_out")
|
|
|
|
}
|
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("unset".to_string());
|
|
|
|
HttpResponse::Ok().body(site_url)
|
|
|
|
} else {
|
|
|
|
HttpResponse::Forbidden().body("logged_out")
|
|
|
|
}
|
2023-04-03 18:50:23 +00:00
|
|
|
}
|
|
|
|
|
2023-04-03 03:26:23 +00:00
|
|
|
// 404 error page
|
|
|
|
#[get("/err/404")]
|
|
|
|
async fn error404() -> impl Responder {
|
|
|
|
NamedFile::open_async("./resources/404.html").await
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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();
|
2023-04-03 23:52:17 +00:00
|
|
|
let longlink = utils::get_longurl(shortlink_str, &data.db);
|
2023-04-03 20:46:22 +00:00
|
|
|
if longlink == "".to_string() {
|
2023-04-03 03:26:23 +00:00
|
|
|
Redirect::to("/err/404")
|
|
|
|
} else {
|
2023-04-03 23:52:17 +00:00
|
|
|
database::add_hit(shortlink.as_str(), &data.db);
|
2023-04-03 03:26:23 +00:00
|
|
|
Redirect::to(longlink).permanent()
|
|
|
|
}
|
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 {
|
2023-04-08 20:36:33 +00:00
|
|
|
if req == env::var("password").unwrap_or(req.clone()) {
|
|
|
|
// If no password was provided, match any password
|
|
|
|
session.insert("session-token", auth::gen_token()).unwrap();
|
2023-04-08 07:52:16 +00:00
|
|
|
HttpResponse::Ok().body("Correct password!")
|
|
|
|
} else {
|
|
|
|
eprintln!("Failed login attempt!");
|
|
|
|
HttpResponse::Forbidden().body("Wrong password!")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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) {
|
|
|
|
database::delete_link(shortlink.to_string(), &data.db);
|
|
|
|
HttpResponse::Ok().body("")
|
|
|
|
} else {
|
|
|
|
HttpResponse::Forbidden().body("Wrong password!")
|
|
|
|
}
|
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-08 20:36:33 +00:00
|
|
|
// Generate session key in runtime so that restarts invalidates older logins
|
2023-04-08 07:52:16 +00:00
|
|
|
let secret_key = Key::generate();
|
2023-04-08 20:36:33 +00:00
|
|
|
let db_location = env::var("db_url").unwrap_or("/opt/urls.sqlite".to_string());
|
|
|
|
|
|
|
|
// 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-04-08 07:52:16 +00:00
|
|
|
.wrap(SessionMiddleware::new(
|
|
|
|
CookieSessionStore::default(),
|
|
|
|
secret_key.clone(),
|
|
|
|
))
|
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-04 00:24:33 +00:00
|
|
|
.wrap(middleware::Compress::default())
|
2023-04-03 03:26:23 +00:00
|
|
|
.service(link_handler)
|
|
|
|
.service(error404)
|
2023-04-03 16:55:27 +00:00
|
|
|
.service(getall)
|
2023-04-03 18:50:23 +00:00
|
|
|
.service(siteurl)
|
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-04-04 00:24:33 +00:00
|
|
|
.default_service(Files::new("/", "./resources/").index_file("index.html"))
|
2023-04-02 21:53:55 +00:00
|
|
|
})
|
2023-04-03 03:26:23 +00:00
|
|
|
.bind(("0.0.0.0", 2000))?
|
2023-04-02 21:53:55 +00:00
|
|
|
.run()
|
|
|
|
.await
|
|
|
|
}
|