More
This commit is contained in:
parent
1f68ca0512
commit
3599803589
43 changed files with 3578 additions and 262 deletions
122
server-rs/src/routes/shorten.rs
Normal file
122
server-rs/src/routes/shorten.rs
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use axum::extract::Path;
|
||||
use axum::http::StatusCode;
|
||||
use axum::response::{IntoResponse, Redirect, Response};
|
||||
use axum::Json;
|
||||
use rand::Rng;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tracing::warn;
|
||||
|
||||
use crate::state::AppState;
|
||||
|
||||
const CODE_LEN: usize = 8;
|
||||
const CHARSET: &[u8] = b"abcdefghijklmnopqrstuvwxyz0123456789";
|
||||
|
||||
fn generate_code() -> String {
|
||||
let mut rng = rand::rng();
|
||||
(0..CODE_LEN)
|
||||
.map(|_| CHARSET[rng.random_range(0..CHARSET.len())] as char)
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct ShortenRequest {
|
||||
params: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct ShortenResponse {
|
||||
code: String,
|
||||
url: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct PbRecord {
|
||||
code: String,
|
||||
params: String,
|
||||
}
|
||||
|
||||
pub async fn post_shorten(state: Arc<AppState>, Json(req): Json<ShortenRequest>) -> Response {
|
||||
let pb_url = state.pocketbase_url.trim_end_matches('/');
|
||||
let code = generate_code();
|
||||
|
||||
let record = PbRecord {
|
||||
code: code.clone(),
|
||||
params: req.params,
|
||||
};
|
||||
|
||||
let res = state
|
||||
.http_client
|
||||
.post(format!(
|
||||
"{pb_url}/api/collections/short_urls/records"
|
||||
))
|
||||
.json(&record)
|
||||
.send()
|
||||
.await;
|
||||
|
||||
match res {
|
||||
Ok(resp) if resp.status().is_success() => {
|
||||
let body = ShortenResponse {
|
||||
url: format!("/s/{code}"),
|
||||
code,
|
||||
};
|
||||
Json(body).into_response()
|
||||
}
|
||||
Ok(resp) => {
|
||||
let status = resp.status();
|
||||
let text = resp.text().await.unwrap_or_default();
|
||||
warn!("PocketBase create failed ({status}): {text}");
|
||||
StatusCode::BAD_GATEWAY.into_response()
|
||||
}
|
||||
Err(err) => {
|
||||
warn!("PocketBase request error: {err}");
|
||||
StatusCode::BAD_GATEWAY.into_response()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_short_url(state: Arc<AppState>, Path(code): Path<String>) -> Response {
|
||||
let pb_url = state.pocketbase_url.trim_end_matches('/');
|
||||
|
||||
let filter = format!("code=\"{code}\"");
|
||||
let url = format!(
|
||||
"{pb_url}/api/collections/short_urls/records?filter={}&perPage=1",
|
||||
urlencoding::encode(&filter)
|
||||
);
|
||||
|
||||
let res = state.http_client.get(&url).send().await;
|
||||
|
||||
match res {
|
||||
Ok(resp) if resp.status().is_success() => {
|
||||
let json: serde_json::Value = match resp.json().await {
|
||||
Ok(v) => v,
|
||||
Err(err) => {
|
||||
warn!("Failed to parse PocketBase response: {err}");
|
||||
return StatusCode::INTERNAL_SERVER_ERROR.into_response();
|
||||
}
|
||||
};
|
||||
|
||||
let params = json["items"]
|
||||
.as_array()
|
||||
.and_then(|items| items.first())
|
||||
.and_then(|item| item["params"].as_str());
|
||||
|
||||
match params {
|
||||
Some(params) => {
|
||||
Redirect::temporary(&format!("/dashboard?{params}")).into_response()
|
||||
}
|
||||
None => StatusCode::NOT_FOUND.into_response(),
|
||||
}
|
||||
}
|
||||
Ok(resp) => {
|
||||
let status = resp.status();
|
||||
warn!("PocketBase lookup failed ({status})");
|
||||
StatusCode::BAD_GATEWAY.into_response()
|
||||
}
|
||||
Err(err) => {
|
||||
warn!("PocketBase request error: {err}");
|
||||
StatusCode::BAD_GATEWAY.into_response()
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue