mirror of
https://github.com/SinTan1729/chhoto-url.git
synced 2025-12-06 05:24:25 -08:00
Compare commits
3 Commits
1b8d9b9710
...
54a63092fe
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
54a63092fe | ||
|
|
0c26cdf1b9 | ||
|
|
2bd8f0faf8 |
@@ -11,7 +11,7 @@ use std::{rc::Rc, time::SystemTime};
|
||||
use crate::config::Config;
|
||||
|
||||
// Validate API key
|
||||
pub fn validate_key(key: &str, config: &Config) -> bool {
|
||||
pub fn is_key_valid(key: &str, config: &Config) -> bool {
|
||||
if let Some(api_key) = &config.api_key {
|
||||
// Check if API Key is hashed using Argon2. More algorithms maybe added later.
|
||||
let authorized = if config.hash_algorithm.is_some() {
|
||||
@@ -54,26 +54,26 @@ pub fn gen_key() -> String {
|
||||
}
|
||||
|
||||
// Check if the API key header exists
|
||||
pub fn api_header(req: &HttpRequest) -> Option<&str> {
|
||||
pub fn get_api_header(req: &HttpRequest) -> Option<&str> {
|
||||
req.headers().get("X-API-Key")?.to_str().ok()
|
||||
}
|
||||
|
||||
// Validate a session
|
||||
pub fn validate(session: Session, config: &Config) -> bool {
|
||||
pub fn is_session_valid(session: Session, config: &Config) -> bool {
|
||||
// If there's no password provided, just return true
|
||||
if config.password.is_none() {
|
||||
return true;
|
||||
}
|
||||
|
||||
if let Ok(token) = session.get::<String>("chhoto-url-auth") {
|
||||
check(token.as_deref())
|
||||
is_token_valid(token.as_deref())
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
// Check a token cryptographically
|
||||
fn check(token: Option<&str>) -> bool {
|
||||
fn is_token_valid(token: Option<&str>) -> bool {
|
||||
if let Some(token_body) = token {
|
||||
let token_parts: Rc<[&str]> = token_body.split(';').collect();
|
||||
if token_parts.len() < 2 {
|
||||
|
||||
@@ -175,7 +175,7 @@ pub fn read() -> Config {
|
||||
if use_wal_mode {
|
||||
info!("Using WAL journaling mode for database.");
|
||||
} else {
|
||||
warn!("Using DELETE journaling mode for database. WAL mode is recommended. (Please read the docs.)");
|
||||
warn!("Using DELETE journaling mode for database. WAL mode is recommended.");
|
||||
}
|
||||
let ensure_acid = !var("ensure_acid").is_ok_and(|s| s.trim() == "False");
|
||||
if ensure_acid {
|
||||
|
||||
@@ -181,7 +181,7 @@ pub fn edit_link(
|
||||
}
|
||||
|
||||
// Clean expired links
|
||||
pub fn cleanup(db: &Connection) {
|
||||
pub fn cleanup(db: &Connection, use_wal_mode: bool) {
|
||||
let now = chrono::Utc::now().timestamp();
|
||||
info!("Starting database cleanup.");
|
||||
|
||||
@@ -197,12 +197,22 @@ pub fn cleanup(db: &Connection) {
|
||||
})
|
||||
.expect("Error cleaning expired links.");
|
||||
|
||||
if use_wal_mode {
|
||||
let mut pragma_statement = db
|
||||
.prepare_cached("PRAGMA wal_checkpoint(TRUNCATE)")
|
||||
.expect("Error preparing SQL statement for pragma: wal_checkpoint.");
|
||||
pragma_statement
|
||||
.query_one([], |row| row.get::<usize, isize>(1))
|
||||
.ok()
|
||||
.filter(|&v| v != -1)
|
||||
.expect("Unable to create WAL checkpoint.");
|
||||
}
|
||||
let mut pragma_statement = db
|
||||
.prepare_cached("PRAGMA optimize")
|
||||
.expect("Error preparing SQL statement for pragma optimize.");
|
||||
.expect("Error preparing SQL statement for pragma: optimize.");
|
||||
pragma_statement
|
||||
.execute([])
|
||||
.expect("Unable to optimize database");
|
||||
.expect("Unable to optimize database.");
|
||||
info!("Optimized database.")
|
||||
}
|
||||
|
||||
|
||||
@@ -97,7 +97,7 @@ async fn main() -> Result<()> {
|
||||
let mut interval = time::interval(time::Duration::from_secs(3600));
|
||||
loop {
|
||||
interval.tick().await;
|
||||
database::cleanup(&db);
|
||||
database::cleanup(&db, conf.use_wal_mode);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ use std::env;
|
||||
|
||||
use crate::AppState;
|
||||
use crate::{auth, database};
|
||||
use crate::{auth::validate, utils};
|
||||
use crate::{auth::is_session_valid, utils};
|
||||
|
||||
// Store the version number
|
||||
const VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
@@ -118,7 +118,7 @@ pub async fn add_link(
|
||||
HttpResponse::Unauthorized().json(result)
|
||||
// If password authentication or public mode is used - keeps backwards compatibility
|
||||
} else {
|
||||
let (success, reply, _) = if auth::validate(session, config) {
|
||||
let (success, reply, _) = if auth::is_session_valid(session, config) {
|
||||
utils::add_link(&req, &data.db, config, false)
|
||||
} else if config.public_mode {
|
||||
utils::add_link(&req, &data.db, config, true)
|
||||
@@ -150,7 +150,7 @@ pub async fn getall(
|
||||
} else if result.error {
|
||||
HttpResponse::Unauthorized().json(result)
|
||||
// If password authentication is used - keeps backwards compatibility
|
||||
} else if auth::validate(session, config) {
|
||||
} else if auth::is_session_valid(session, config) {
|
||||
HttpResponse::Ok().body(utils::getall(&data.db, params.into_inner()))
|
||||
} else {
|
||||
HttpResponse::Unauthorized().body("Not logged in!")
|
||||
@@ -196,7 +196,7 @@ pub async fn edit_link(
|
||||
) -> HttpResponse {
|
||||
let config = &data.config;
|
||||
let result = utils::is_api_ok(http, config);
|
||||
if result.success || validate(session, config) {
|
||||
if result.success || is_session_valid(session, config) {
|
||||
if let Some((server_error, error_msg)) = utils::edit_link(&req, &data.db, config) {
|
||||
let body = Response {
|
||||
success: false,
|
||||
@@ -250,7 +250,7 @@ pub async fn whoami(
|
||||
) -> HttpResponse {
|
||||
let config = &data.config;
|
||||
let result = utils::is_api_ok(http, config);
|
||||
let acting_user = if result.success || validate(session, config) {
|
||||
let acting_user = if result.success || is_session_valid(session, config) {
|
||||
"admin"
|
||||
} else if config.public_mode {
|
||||
"public"
|
||||
@@ -269,7 +269,7 @@ pub async fn getconfig(
|
||||
) -> HttpResponse {
|
||||
let config = &data.config;
|
||||
let result = utils::is_api_ok(http, config);
|
||||
if result.success || validate(session, config) || data.config.public_mode {
|
||||
if result.success || is_session_valid(session, config) || data.config.public_mode {
|
||||
let backend_config = BackendConfig {
|
||||
version: VERSION.to_string(),
|
||||
allow_capital_letters: config.allow_capital_letters,
|
||||
@@ -424,7 +424,7 @@ pub async fn delete_link(
|
||||
} else if result.error {
|
||||
HttpResponse::Unauthorized().json(result)
|
||||
// If "pass" is true - keeps backwards compatibility
|
||||
} else if auth::validate(session, config) {
|
||||
} else if auth::is_session_valid(session, config) {
|
||||
if utils::delete_link(&shortlink, &data.db, data.config.allow_capital_letters) {
|
||||
HttpResponse::Ok().body(format!("Deleted {shortlink}"))
|
||||
} else {
|
||||
|
||||
@@ -42,9 +42,9 @@ pub fn is_api_ok(http: HttpRequest, config: &Config) -> Response {
|
||||
// If the api_key environment variable exists
|
||||
if config.api_key.is_some() {
|
||||
// If the header exists
|
||||
if let Some(header) = auth::api_header(&http) {
|
||||
if let Some(header) = auth::get_api_header(&http) {
|
||||
// If the header is correct
|
||||
if auth::validate_key(header, config) {
|
||||
if auth::is_key_valid(header, config) {
|
||||
Response {
|
||||
success: true,
|
||||
error: false,
|
||||
@@ -72,7 +72,7 @@ pub fn is_api_ok(http: HttpRequest, config: &Config) -> Response {
|
||||
}
|
||||
} else {
|
||||
// If the API key isn't set, but an API Key header is provided
|
||||
if auth::api_header(&http).is_some() {
|
||||
if auth::get_api_header(&http).is_some() {
|
||||
Response {
|
||||
success: false,
|
||||
error: true,
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
# SPDX-FileCopyrightText: 2025 Sayantan Santra <sayantan.santra689@gmail.com>
|
||||
# SPDX-License-Identifier: MIT
|
||||
#
|
||||
# chhoto-url.container
|
||||
#
|
||||
# To be used with rootless quadlets. Put inside your $XDG_CONFIG_HOME/containers/systemd/
|
||||
# Take a look at README for the explanation of the configs.
|
||||
# The commented out configs are optional.
|
||||
|
||||
Reference in New Issue
Block a user